Skip to main content

torvyn_cli/output/
terminal.rs

1//! Human-readable terminal output helpers.
2//!
3//! Provides styled output primitives used by command handlers to render
4//! results in a visually clear, consistent format.
5
6use crate::output::OutputContext;
7
8/// Print a success checkmark with a message.
9pub fn print_success(ctx: &OutputContext, message: &str) {
10    if ctx.color_enabled {
11        eprintln!("{} {message}", console::style("\u{2713}").green().bold());
12    } else {
13        eprintln!("[ok] {message}");
14    }
15}
16
17/// Print a failure cross with a message.
18pub fn print_failure(ctx: &OutputContext, message: &str) {
19    if ctx.color_enabled {
20        eprintln!("{} {message}", console::style("\u{2717}").red().bold());
21    } else {
22        eprintln!("[FAIL] {message}");
23    }
24}
25
26/// Print a header line (e.g., "── Throughput ───────").
27#[allow(dead_code)]
28pub fn print_header(ctx: &OutputContext, title: &str) {
29    let width = ctx.term_width as usize;
30    let pad_len = width.saturating_sub(title.len() + 6);
31    let line = "\u{2500}".repeat(pad_len.min(60));
32    if ctx.color_enabled {
33        eprintln!(
34            "\n  {} {} {}",
35            console::style("\u{2500}\u{2500}").dim(),
36            console::style(title).bold(),
37            console::style(line).dim()
38        );
39    } else {
40        eprintln!("\n  -- {title} {}", "-".repeat(pad_len.min(60)));
41    }
42}
43
44/// Print a key-value pair indented by 2 spaces.
45pub fn print_kv(ctx: &OutputContext, key: &str, value: &str) {
46    if ctx.color_enabled {
47        eprintln!("  {}  {value}", console::style(format!("{key}:")).dim());
48    } else {
49        eprintln!("  {key}:  {value}");
50    }
51}
52
53/// Print a directory tree.
54///
55/// # Parameters
56/// - `entries`: List of (indent_level, name, is_last_sibling) tuples.
57pub fn print_tree(ctx: &OutputContext, entries: &[(usize, &str, bool)]) {
58    for (indent, name, is_last) in entries {
59        let prefix: String = if *indent == 0 {
60            String::new()
61        } else {
62            let mut p = String::new();
63            for _ in 0..(*indent - 1) {
64                p.push_str("\u{2502}   ");
65            }
66            if *is_last {
67                p.push_str("\u{2514}\u{2500}\u{2500} ");
68            } else {
69                p.push_str("\u{251c}\u{2500}\u{2500} ");
70            }
71            p
72        };
73        if ctx.color_enabled {
74            eprintln!("  {}{}", console::style(&prefix).dim(), name);
75        } else {
76            eprintln!("  {prefix}{name}");
77        }
78    }
79}
80
81/// Format bytes into human-readable string (e.g., "4.2 MiB").
82pub fn format_bytes(bytes: u64) -> String {
83    if bytes < 1024 {
84        format!("{bytes} B")
85    } else if bytes < 1024 * 1024 {
86        format!("{:.1} KiB", bytes as f64 / 1024.0)
87    } else if bytes < 1024 * 1024 * 1024 {
88        format!("{:.1} MiB", bytes as f64 / (1024.0 * 1024.0))
89    } else {
90        format!("{:.1} GiB", bytes as f64 / (1024.0 * 1024.0 * 1024.0))
91    }
92}