You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
95 lines
2.8 KiB
Rust
95 lines
2.8 KiB
Rust
//! ANSI color helpers for terminal output.
|
|
//!
|
|
//! All functions take a `color_enabled` flag; when `false` the string is
|
|
//! returned unchanged so callers never need to branch on their own.
|
|
|
|
/// Wrap `s` in ANSI escape codes when `color_enabled` is true.
|
|
///
|
|
/// `open` is the escape sequence to start the color and `RESET` (`\x1b[0m`)
|
|
/// is appended automatically.
|
|
fn colorize(s: &str, open: &str, color_enabled: bool) -> String {
|
|
if color_enabled {
|
|
format!("{open}{s}\x1b[0m")
|
|
} else {
|
|
s.to_string()
|
|
}
|
|
}
|
|
|
|
/// Orange — used for `[assistant]` labels.
|
|
///
|
|
/// Uses 256-color code 208 (bright orange).
|
|
pub fn orange(s: &str, color_enabled: bool) -> String {
|
|
colorize(s, "\x1b[38;5;208m", color_enabled)
|
|
}
|
|
|
|
/// Grey — used for `[user]` labels.
|
|
///
|
|
/// Uses 256-color code 245 (mid-grey).
|
|
pub fn grey(s: &str, color_enabled: bool) -> String {
|
|
colorize(s, "\x1b[38;5;245m", color_enabled)
|
|
}
|
|
|
|
/// Blue — used for `[tool: X]` labels.
|
|
///
|
|
/// Uses 256-color code 33 (dodger blue).
|
|
pub fn blue(s: &str, color_enabled: bool) -> String {
|
|
colorize(s, "\x1b[38;5;33m", color_enabled)
|
|
}
|
|
|
|
/// Green — used for `[tool_result]` labels.
|
|
///
|
|
/// Uses 256-color code 34 (green).
|
|
pub fn green(s: &str, color_enabled: bool) -> String {
|
|
colorize(s, "\x1b[32m", color_enabled)
|
|
}
|
|
|
|
/// Red — used for `[tool_result (error)]` labels.
|
|
///
|
|
/// Uses standard ANSI red (code 31).
|
|
pub fn red(s: &str, color_enabled: bool) -> String {
|
|
colorize(s, "\x1b[31m", color_enabled)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
/// When color is disabled, the string is returned unchanged.
|
|
#[test]
|
|
fn color_disabled_returns_plain_string() {
|
|
assert_eq!(orange("hello", false), "hello");
|
|
assert_eq!(grey("hello", false), "hello");
|
|
assert_eq!(blue("hello", false), "hello");
|
|
assert_eq!(green("hello", false), "hello");
|
|
assert_eq!(red("hello", false), "hello");
|
|
}
|
|
|
|
/// When color is enabled, output contains the escape code and a reset.
|
|
#[test]
|
|
fn color_enabled_wraps_with_escapes() {
|
|
let out = orange("[assistant]", true);
|
|
assert!(out.starts_with("\x1b["), "should start with escape");
|
|
assert!(out.ends_with("\x1b[0m"), "should end with reset");
|
|
assert!(out.contains("[assistant]"), "should contain original text");
|
|
}
|
|
|
|
/// Each color function produces distinct output.
|
|
#[test]
|
|
fn each_color_function_is_distinct() {
|
|
let text = "X";
|
|
let colors = [
|
|
orange(text, true),
|
|
grey(text, true),
|
|
blue(text, true),
|
|
green(text, true),
|
|
red(text, true),
|
|
];
|
|
// All pairs must differ.
|
|
for i in 0..colors.len() {
|
|
for j in (i + 1)..colors.len() {
|
|
assert_ne!(colors[i], colors[j], "colors[{i}] == colors[{j}]");
|
|
}
|
|
}
|
|
}
|
|
}
|