Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions rust/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target
234 changes: 234 additions & 0 deletions rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 27 additions & 0 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[package]
name = "tokenline"
version = "0.1.0"
edition = "2024"
rust-version = "1.85"
description = "A cache-aware statusline for AI coding CLIs"
license = "MIT"
repository = "https://github.com/inbrace-tech/tokenline"

[[bin]]
name = "tokenline"
path = "src/main.rs"

[lib]
name = "tokenline"
path = "src/lib.rs"

[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
jiff = "0.2"

[profile.release]
opt-level = "z"
lto = true
strip = true
panic = "unwind" # catch_unwind MUST work; abort would be a non-zero exit
106 changes: 106 additions & 0 deletions rust/src/fmt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//! Formatting helpers, byte-for-byte parity with `tokenline.sh`.
//!
//! Carries `fmt_k` / `fmt_eta` and the 256-color palette; the ISO-8601 → epoch
//! parser lands in the next slice.

/// 256-color SGR sequences, byte-identical to `tokenline.sh`'s `COLOR_*` /
/// `STYLE_*` constants (`tokenline.sh:21-30`). Byte parity matters: the golden
/// oracle diffs the raw escape sequences, so `RESET` is `[00m` (two digits),
/// not `[0m`, exactly as the bash emits.
pub mod color {
pub const GRAY: &str = "\x1b[38;5;244m";
pub const DARK_GRAY: &str = "\x1b[38;5;240m";
pub const CYAN: &str = "\x1b[38;5;51m";
pub const YELLOW: &str = "\x1b[38;5;226m";
pub const MAGENTA: &str = "\x1b[38;5;201m";
pub const ORANGE: &str = "\x1b[38;5;208m";
pub const RED: &str = "\x1b[38;5;196m";
pub const GREEN: &str = "\x1b[38;5;46m";
pub const RESET: &str = "\x1b[00m";
pub const BLINK: &str = "\x1b[1;5m";
}

/// Compact token count, mirroring the bash `fmt_k` (awk `%.1f`):
/// `1_500_000 -> "1.5M"`, `25_600 -> "25.6k"`, `900 -> "900"`.
pub fn fmt_k(v: u64) -> String {
if v >= 1_000_000 {
format!("{:.1}M", v as f64 / 1_000_000.0)
} else if v >= 1_000 {
format!("{:.1}k", v as f64 / 1_000.0)
} else {
v.to_string()
}
}

/// Human ETA from raw seconds, mirroring the bash `fmt_eta`:
/// `<=0 -> "now"`, `<1h -> "%dm"`, `<1d -> "%dh%dm"/"%dh"`, else `"%dd%dh"/"%dd"`.
pub fn fmt_eta(secs: i64) -> String {
if secs <= 0 {
return "now".to_string();
}
if secs < 3600 {
return format!("{}m", secs / 60);
}
if secs < 86_400 {
let (h, m) = (secs / 3600, (secs % 3600) / 60);
return if m > 0 {
format!("{}h{}m", h, m)
} else {
format!("{}h", h)
};
}
let (d, h) = (secs / 86_400, (secs % 86_400) / 3600);
if h > 0 {
format!("{}d{}h", d, h)
} else {
format!("{}d", d)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn fmt_k_tiers() {
assert_eq!(fmt_k(0), "0");
assert_eq!(fmt_k(900), "900");
assert_eq!(fmt_k(999), "999");
assert_eq!(fmt_k(1_000), "1.0k");
assert_eq!(fmt_k(2_000), "2.0k");
assert_eq!(fmt_k(2_200), "2.2k");
assert_eq!(fmt_k(25_600), "25.6k");
assert_eq!(fmt_k(86_600), "86.6k");
assert_eq!(fmt_k(1_000_000), "1.0M");
assert_eq!(fmt_k(1_500_000), "1.5M");
}

#[test]
fn palette_matches_bash_bytes() {
// Exact bytes captured from `tokenline.sh` output (cat -v): ESC[38;5;Nm.
assert_eq!(color::GRAY, "\x1b[38;5;244m");
assert_eq!(color::CYAN, "\x1b[38;5;51m");
assert_eq!(color::YELLOW, "\x1b[38;5;226m");
assert_eq!(color::MAGENTA, "\x1b[38;5;201m");
assert_eq!(color::ORANGE, "\x1b[38;5;208m");
assert_eq!(color::GREEN, "\x1b[38;5;46m");
assert_eq!(color::RED, "\x1b[38;5;196m");
assert_eq!(color::DARK_GRAY, "\x1b[38;5;240m");
// bash COLOR_RESET is [00m (two digits), STYLE_BLINK is [1;5m.
assert_eq!(color::RESET, "\x1b[00m");
assert_eq!(color::BLINK, "\x1b[1;5m");
}

#[test]
fn fmt_eta_tiers() {
assert_eq!(fmt_eta(-5), "now");
assert_eq!(fmt_eta(0), "now");
assert_eq!(fmt_eta(30), "0m"); // %dm with integer division, like bash
assert_eq!(fmt_eta(90), "1m");
assert_eq!(fmt_eta(3_600), "1h");
assert_eq!(fmt_eta(3_660), "1h1m");
assert_eq!(fmt_eta(86_400), "1d");
assert_eq!(fmt_eta(90_000), "1d1h");
assert_eq!(fmt_eta(273_600), "3d4h"); // 5h-window reset ETA in the golden
}
}
6 changes: 6 additions & 0 deletions rust/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
//! tokenline — a cache-aware statusline for AI coding CLIs.
//!
//! Pure domain (deterministic, golden-testable) and a thin stateful edge.
//! Modules land bottom-up, one slice per PR (see inbrace-tech/tokenline#27).

pub mod fmt;
6 changes: 6 additions & 0 deletions rust/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// tokenline — a cache-aware statusline for AI coding CLIs.
//
// Slice 0: scaffold only. The real edge (stdin → now → cache_dir → render →
// print → exit(0), with catch_unwind so a panic never crashes the host) lands
// in the final slice; the pure domain modules land bottom-up before it.
fn main() {}