tempo_cli/cli/
formatter.rs

1pub struct CliFormatter;
2
3impl CliFormatter {
4    pub fn print_section_header(title: &str) {
5        println!("\n{}", ansi_color("cyan", title, true));
6        println!("{}", "─".repeat(title.len()).dimmed());
7    }
8
9    pub fn print_field(label: &str, value: &str, color: Option<&str>) {
10        let colored_value = match color {
11            Some(c) => ansi_color(c, value, false),
12            None => value.to_string(),
13        };
14        println!("  {:<12} {}", format!("{}:", label).dimmed(), colored_value);
15    }
16
17    pub fn print_field_bold(label: &str, value: &str, color: Option<&str>) {
18        let colored_value = match color {
19            Some(c) => ansi_color(c, value, true),
20            None => bold(value),
21        };
22        println!("  {:<12} {}", format!("{}:", label).dimmed(), colored_value);
23    }
24
25    pub fn print_status(status: &str, is_active: bool) {
26        let (symbol, color) = if is_active {
27            ("●", "green")
28        } else {
29            ("○", "red")
30        };
31        println!(
32            "  {:<12} {} {}",
33            "Status:".dimmed(),
34            ansi_color(color, symbol, false),
35            ansi_color(color, status, true)
36        );
37    }
38
39    pub fn print_summary(title: &str, total: &str) {
40        println!("\n{}", ansi_color("white", title, true));
41        println!("  {}", ansi_color("green", total, true));
42    }
43
44    pub fn print_project_entry(name: &str, duration: &str) {
45        println!(
46            "  {:<25} {}",
47            ansi_color("yellow", &truncate_string(name, 25), true),
48            ansi_color("green", duration, true)
49        );
50    }
51
52    pub fn print_context_entry(context: &str, duration: &str) {
53        let color = get_context_color(context);
54        println!(
55            "    {:<20} {}",
56            ansi_color(color, &format!("├─ {}", context), false),
57            ansi_color("green", duration, false)
58        );
59    }
60
61    pub fn print_session_entry(
62        session_id: Option<i64>,
63        project: &str,
64        duration: &str,
65        status: &str,
66        timestamp: &str,
67    ) {
68        let status_symbol = if status == "active" { "●" } else { "○" };
69        let status_color = if status == "active" { "green" } else { "gray" };
70
71        println!(
72            "  {} {:<20} {:<15} {}",
73            ansi_color(status_color, status_symbol, false),
74            ansi_color("yellow", &truncate_string(project, 20), false),
75            ansi_color("green", duration, false),
76            timestamp.dimmed()
77        );
78    }
79
80    pub fn print_empty_state(message: &str) {
81        println!("\n  {}", message.dimmed());
82    }
83
84    pub fn print_error(message: &str) {
85        println!(
86            "  {} {}",
87            ansi_color("red", "✗", true),
88            ansi_color("red", message, false)
89        );
90    }
91
92    pub fn print_success(message: &str) {
93        println!(
94            "  {} {}",
95            ansi_color("green", "✓", true),
96            ansi_color("green", message, false)
97        );
98    }
99
100    pub fn print_warning(message: &str) {
101        println!(
102            "  {} {}",
103            ansi_color("yellow", "⚠", true),
104            ansi_color("yellow", message, false)
105        );
106    }
107
108    pub fn print_info(message: &str) {
109        println!("  {} {}", ansi_color("cyan", "ℹ", true), message);
110    }
111}
112
113// Helper functions
114pub fn ansi_color(color: &str, text: &str, bold: bool) -> String {
115    let color_code = match color {
116        "red" => "31",
117        "green" => "32",
118        "yellow" => "33",
119        "blue" => "34",
120        "magenta" => "35",
121        "cyan" => "36",
122        "white" => "37",
123        "gray" => "90",
124        _ => "37", // default to white
125    };
126
127    if bold {
128        format!("\x1b[1;{}m{}\x1b[0m", color_code, text)
129    } else {
130        format!("\x1b[{}m{}\x1b[0m", color_code, text)
131    }
132}
133
134fn bold(text: &str) -> String {
135    format!("\x1b[1m{}\x1b[0m", text)
136}
137
138pub trait StringFormat {
139    fn dimmed(&self) -> String;
140}
141
142impl StringFormat for str {
143    fn dimmed(&self) -> String {
144        format!("\x1b[2m{}\x1b[0m", self)
145    }
146}
147
148fn get_context_color(context: &str) -> &str {
149    match context {
150        "terminal" => "cyan",
151        "ide" => "magenta",
152        "linked" => "yellow",
153        "manual" => "blue",
154        _ => "white",
155    }
156}
157
158pub fn truncate_string(s: &str, max_len: usize) -> String {
159    if s.len() <= max_len {
160        s.to_string()
161    } else {
162        format!("{}…", &s[..max_len.saturating_sub(1)])
163    }
164}
165
166pub fn format_duration_clean(seconds: i64) -> String {
167    let hours = seconds / 3600;
168    let minutes = (seconds % 3600) / 60;
169    let secs = seconds % 60;
170
171    if hours > 0 {
172        format!("{}h {}m", hours, minutes)
173    } else if minutes > 0 {
174        format!("{}m {}s", minutes, secs)
175    } else {
176        format!("{}s", secs)
177    }
178}