use owo_colors::OwoColorize;
use std::io::{self, Write};
pub struct Output {
pub colored: bool,
}
impl Default for Output {
fn default() -> Self {
Self::new()
}
}
impl Output {
pub fn new() -> Self {
Self { colored: true }
}
pub fn no_color() -> Self {
Self { colored: false }
}
pub fn banner(&self) {
if self.colored {
println!(
r#"
{}
{}
{}
{}
{}
"#,
" _ ____ _____ ____ ".bright_cyan().bold(),
" / \\ | _ \\| ____/ ___| ".bright_cyan().bold(),
" / _ \\ | |_) | _| \\___ \\ ".cyan().bold(),
" / ___ \\| _ <| |___ ___) |".blue().bold(),
"/_/ \\_\\_| \\_\\_____|____/ ".blue().bold(),
);
println!(
" {} {}\n",
"Agentic Retrieval Enhanced Server".bright_white().bold(),
format!("v{}", env!("CARGO_PKG_VERSION")).dimmed()
);
} else {
println!(
r#"
_ ____ _____ ____
/ \ | _ \| ____/ ___|
/ _ \ | |_) | _| \___ \
/ ___ \| _ <| |___ ___) |
/_/ \_\_| \_\_____|____/
Agentic Retrieval Enhanced Server v{}
"#,
env!("CARGO_PKG_VERSION")
);
}
}
pub fn success(&self, message: &str) {
if self.colored {
println!(" {} {}", "✓".green().bold(), message.green());
} else {
println!(" [OK] {}", message);
}
}
pub fn info(&self, message: &str) {
if self.colored {
println!(" {} {}", "•".blue(), message);
} else {
println!(" [INFO] {}", message);
}
}
pub fn warning(&self, message: &str) {
if self.colored {
println!(" {} {}", "⚠".yellow().bold(), message.yellow());
} else {
println!(" [WARN] {}", message);
}
}
pub fn error(&self, message: &str) {
if self.colored {
eprintln!(" {} {}", "✗".red().bold(), message.red());
} else {
eprintln!(" [ERROR] {}", message);
}
}
pub fn step(&self, step_num: u32, total: u32, message: &str) {
if self.colored {
println!(
" {} {}",
format!("[{}/{}]", step_num, total).dimmed(),
message.bright_white()
);
} else {
println!(" [{}/{}] {}", step_num, total, message);
}
}
pub fn created(&self, file_type: &str, path: &str) {
if self.colored {
println!(
" {} {} {}",
"✓".green().bold(),
file_type.dimmed(),
path.bright_white()
);
} else {
println!(" [CREATED] {} {}", file_type, path);
}
}
pub fn skipped(&self, path: &str, reason: &str) {
if self.colored {
println!(
" {} {} {}",
"○".yellow(),
path.dimmed(),
format!("({})", reason).yellow()
);
} else {
println!(" [SKIPPED] {} ({})", path, reason);
}
}
pub fn created_dir(&self, path: &str) {
if self.colored {
println!(
" {} {} {}",
"✓".green().bold(),
"directory".dimmed(),
path.bright_white()
);
} else {
println!(" [CREATED] directory {}", path);
}
}
pub fn header(&self, title: &str) {
if self.colored {
println!("\n {}", title.bright_white().bold().underline());
} else {
println!("\n === {} ===", title);
}
}
pub fn subheader(&self, title: &str) {
if self.colored {
println!("\n {}", title.cyan().bold());
} else {
println!("\n --- {} ---", title);
}
}
pub fn kv(&self, key: &str, value: &str) {
if self.colored {
println!(" {}: {}", key.dimmed(), value.bright_white());
} else {
println!(" {}: {}", key, value);
}
}
pub fn list_item(&self, item: &str) {
if self.colored {
println!(" {} {}", "•".blue(), item);
} else {
println!(" - {}", item);
}
}
pub fn hint(&self, message: &str) {
if self.colored {
println!("\n {} {}", "💡".dimmed(), message.dimmed().italic());
} else {
println!("\n [TIP] {}", message);
}
}
pub fn command(&self, cmd: &str) {
if self.colored {
println!(" {}", format!("$ {}", cmd).bright_cyan());
} else {
println!(" $ {}", cmd);
}
}
pub fn complete(&self, message: &str) {
if self.colored {
println!("\n {} {}", "🚀".green(), message.bright_green().bold());
} else {
println!("\n [DONE] {}", message);
}
}
pub fn confirm(&self, message: &str) -> bool {
if self.colored {
print!(
" {} {} [y/N]: ",
"?".bright_yellow().bold(),
message.bright_white()
);
} else {
print!(" [?] {} [y/N]: ", message);
}
io::stdout().flush().ok();
let mut input = String::new();
if io::stdin().read_line(&mut input).is_ok() {
let input = input.trim().to_lowercase();
input == "y" || input == "yes"
} else {
false
}
}
pub fn table_header(&self, columns: &[&str]) {
if self.colored {
let header: String = columns
.iter()
.map(|c| format!("{:<15}", c))
.collect::<Vec<_>>()
.join(" ");
println!(" {}", header.bright_white().bold());
println!(" {}", "─".repeat(columns.len() * 16).dimmed());
} else {
let header: String = columns
.iter()
.map(|c| format!("{:<15}", c))
.collect::<Vec<_>>()
.join(" ");
println!(" {}", header);
println!(" {}", "-".repeat(columns.len() * 16));
}
}
pub fn table_row(&self, values: &[&str]) {
let row: String = values
.iter()
.map(|v| format!("{:<15}", v))
.collect::<Vec<_>>()
.join(" ");
println!(" {}", row);
}
pub fn newline(&self) {
println!();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_output_new() {
let output = Output::new();
assert!(output.colored);
}
#[test]
fn test_output_no_color() {
let output = Output::no_color();
assert!(!output.colored);
}
#[test]
fn test_output_default() {
let output = Output::default();
assert!(output.colored);
}
#[test]
fn test_confirm_parsing() {
let output = Output::new();
assert!(output.colored);
let output_no_color = Output::no_color();
assert!(!output_no_color.colored);
}
#[test]
fn test_table_row_formatting() {
let output = Output::no_color();
output.table_row(&["a", "b", "c"]);
output.table_row(&["long_value_here", "another", "third"]);
output.table_row(&[]);
}
#[test]
fn test_table_header_formatting() {
let output = Output::no_color();
output.table_header(&["Name", "Model", "Tools"]);
output.table_header(&["Single"]);
output.table_header(&[]);
}
#[test]
fn test_output_methods_no_panic() {
let output = Output::no_color();
output.success("test success");
output.info("test info");
output.warning("test warning");
output.error("test error");
output.step(1, 3, "step message");
output.created("file", "path/to/file");
output.skipped("path", "reason");
output.created_dir("some/dir");
output.header("Test Header");
output.subheader("Test Subheader");
output.kv("key", "value");
output.list_item("item");
output.hint("hint message");
output.command("some command");
output.complete("complete message");
output.newline();
}
#[test]
fn test_output_methods_colored_no_panic() {
let output = Output::new();
output.success("test success");
output.info("test info");
output.warning("test warning");
output.error("test error");
output.step(1, 3, "step message");
output.created("file", "path/to/file");
output.skipped("path", "reason");
output.created_dir("some/dir");
output.header("Test Header");
output.subheader("Test Subheader");
output.kv("key", "value");
output.list_item("item");
output.hint("hint message");
output.command("some command");
output.complete("complete message");
output.newline();
output.banner();
}
}