theater_cli/utils/
formatting.rs

1use console::style;
2use std::time::Duration;
3use theater::id::TheaterId;
4use theater::messages::ActorStatus;
5use theater::ChainEvent;
6
7/// Format an actor ID in a consistent way
8pub fn format_id(id: &TheaterId) -> String {
9    style(id.to_string()).cyan().to_string()
10}
11
12/// Format a short version of an actor ID (first 8 chars)
13pub fn format_short_id(id: &TheaterId) -> String {
14    let id_str = id.to_string();
15    let short_id = &id_str[..std::cmp::min(8, id_str.len())];
16    style(short_id).cyan().to_string()
17}
18
19/// Format an actor status with appropriate color
20pub fn format_status(status: &ActorStatus) -> String {
21    match status {
22        ActorStatus::Running => style("RUNNING").green().bold().to_string(),
23        ActorStatus::Stopped => style("STOPPED").red().bold().to_string(),
24        ActorStatus::Failed => style("FAILED").red().bold().to_string(),
25    }
26}
27
28/// Format a timestamp as a human-readable date/time
29pub fn format_timestamp(timestamp: &u64) -> String {
30    let datetime = chrono::DateTime::from_timestamp(*timestamp as i64, 0)
31        .unwrap_or_else(|| chrono::DateTime::UNIX_EPOCH);
32    datetime.format("%Y-%m-%d %H:%M:%S").to_string()
33}
34
35/// Format a duration in a human-readable form
36pub fn format_duration(duration: Duration) -> String {
37    let total_secs = duration.as_secs();
38
39    if total_secs < 60 {
40        return format!("{}s", total_secs);
41    }
42
43    let mins = total_secs / 60;
44    let secs = total_secs % 60;
45
46    if mins < 60 {
47        return format!("{}m {}s", mins, secs);
48    }
49
50    let hours = mins / 60;
51    let mins = mins % 60;
52
53    if hours < 24 {
54        return format!("{}h {}m {}s", hours, mins, secs);
55    }
56
57    let days = hours / 24;
58    let hours = hours % 24;
59
60    format!("{}d {}h {}m {}s", days, hours, mins, secs)
61}
62
63/// Format a byte array as a hex string with optional shortening
64pub fn format_hash(hash: &[u8], shorten: bool) -> String {
65    let hex = hex::encode(hash);
66    if shorten && hex.len() > 16 {
67        format!("{}..{}", &hex[0..8], &hex[hex.len() - 8..])
68    } else {
69        hex
70    }
71}
72
73/// Format a section header
74pub fn format_section(title: &str) -> String {
75    format!(
76        "\n{}\n{}",
77        style(title).bold().underlined(),
78        style("─".repeat(title.len())).dim()
79    )
80}
81
82/// Format a key-value pair for display
83pub fn format_key_value(key: &str, value: &str) -> String {
84    format!("{}: {}", style(key).bold(), value)
85}
86
87/// Format an event summary
88pub fn format_event_summary(event: &ChainEvent) -> String {
89    let event_type = style(&event.event_type).yellow();
90    let timestamp = format_timestamp(&event.timestamp);
91    let hash = format_hash(&event.hash, true);
92
93    format!(
94        "{} at {} (hash: {})",
95        event_type,
96        style(timestamp).dim(),
97        style(hash).dim()
98    )
99}
100
101/// Create a table with headers and rows
102#[allow(dead_code)]
103pub fn format_table(headers: &[&str], rows: &[Vec<String>], indent: usize) -> String {
104    if rows.is_empty() {
105        return "No data available".to_string();
106    }
107
108    // Calculate column widths
109    let mut widths: Vec<usize> = headers.iter().map(|h| h.len()).collect();
110
111    for row in rows {
112        for (i, cell) in row.iter().enumerate() {
113            if i < widths.len() {
114                widths[i] = std::cmp::max(widths[i], cell.len());
115            }
116        }
117    }
118
119    // Format the header
120    let mut result = " ".repeat(indent);
121    for (i, header) in headers.iter().enumerate() {
122        result.push_str(&format!(
123            "{:<width$} ",
124            style(*header).bold(),
125            width = widths[i]
126        ));
127    }
128    result.push('\n');
129
130    // Add separator
131    result.push_str(&" ".repeat(indent));
132    for width in &widths {
133        result.push_str(&style("─".repeat(*width)).dim().to_string());
134        result.push(' ');
135    }
136    result.push('\n');
137
138    // Format rows
139    for row in rows {
140        result.push_str(&" ".repeat(indent));
141        for (i, cell) in row.iter().enumerate() {
142            if i < widths.len() {
143                result.push_str(&format!("{:<width$} ", cell, width = widths[i]));
144            }
145        }
146        result.push('\n');
147    }
148
149    result
150}
151
152/// Format a success message
153pub fn format_success(message: &str) -> String {
154    format!("{} {}", style("✓").green().bold(), message)
155}
156
157/// Format an error message
158pub fn format_error(message: &str) -> String {
159    format!("{} {}", style("✗").red().bold(), message)
160}
161
162/// Format a warning message
163pub fn format_info(message: &str) -> String {
164    format!("{} {}", style("ℹ").blue().bold(), message)
165}
166
167/// Format a warning message
168pub fn format_warning(message: &str) -> String {
169    format!("{} {}", style("⚠").yellow().bold(), message)
170}