git_x/core/
output.rs

1use console::style;
2use std::fmt;
3
4/// Buffered output utility for better performance
5pub struct BufferedOutput {
6    lines: Vec<String>,
7}
8
9impl BufferedOutput {
10    /// Create a new buffered output
11    pub fn new() -> Self {
12        Self { lines: Vec::new() }
13    }
14
15    /// Add a line to the buffer
16    pub fn add_line(&mut self, line: String) {
17        self.lines.push(line);
18    }
19
20    /// Add multiple lines to the buffer
21    pub fn add_lines(&mut self, lines: Vec<String>) {
22        self.lines.extend(lines);
23    }
24
25    /// Add a formatted line to the buffer
26    pub fn add_formatted(&mut self, line: String) {
27        self.lines.push(line);
28    }
29
30    /// Get all buffered content as a single string
31    pub fn content(&self) -> String {
32        self.lines.join("\n")
33    }
34
35    /// Print all buffered content to stdout
36    pub fn flush(&self) {
37        if !self.lines.is_empty() {
38            println!("{}", self.content());
39        }
40    }
41
42    /// Print all buffered content to stderr
43    pub fn flush_err(&self) {
44        if !self.lines.is_empty() {
45            eprintln!("{}", self.content());
46        }
47    }
48
49    /// Get the number of buffered lines
50    pub fn len(&self) -> usize {
51        self.lines.len()
52    }
53
54    /// Check if buffer is empty
55    pub fn is_empty(&self) -> bool {
56        self.lines.is_empty()
57    }
58}
59
60impl Default for BufferedOutput {
61    fn default() -> Self {
62        Self::new()
63    }
64}
65
66impl fmt::Display for BufferedOutput {
67    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68        write!(f, "{}", self.lines.join("\n"))
69    }
70}
71
72/// Common formatting utilities
73pub struct Format;
74
75impl Format {
76    /// Format an error message with emoji
77    pub fn error(msg: &str) -> String {
78        format!("{} {}", style("❌").bold(), msg)
79    }
80
81    /// Format a success message with emoji
82    pub fn success(msg: &str) -> String {
83        format!("{} {}", style("✅").bold(), msg)
84    }
85
86    /// Format an info message with emoji
87    pub fn info(msg: &str) -> String {
88        format!("{} {}", style("ℹ️").bold(), msg)
89    }
90
91    /// Format a warning message with emoji
92    pub fn warning(msg: &str) -> String {
93        format!("{} {}", style("⚠️").bold().yellow(), msg)
94    }
95
96    /// Format text with bold styling
97    pub fn bold(text: &str) -> String {
98        style(text).bold().to_string()
99    }
100
101    /// Format text with color
102    pub fn colored(text: &str, color: console::Color) -> String {
103        style(text).fg(color).to_string()
104    }
105}
106
107/// Output formatters for different data types
108pub struct TableFormatter {
109    headers: Vec<String>,
110    rows: Vec<Vec<String>>,
111}
112
113impl TableFormatter {
114    pub fn new(headers: Vec<String>) -> Self {
115        Self {
116            headers,
117            rows: Vec::new(),
118        }
119    }
120
121    pub fn add_row(&mut self, row: Vec<String>) {
122        self.rows.push(row);
123    }
124
125    pub fn format(&self) -> String {
126        if self.rows.is_empty() {
127            return "No data to display".to_string();
128        }
129
130        let mut output = String::new();
131
132        // Calculate column widths
133        let mut widths: Vec<usize> = self.headers.iter().map(|h| h.len()).collect();
134        for row in &self.rows {
135            for (i, cell) in row.iter().enumerate() {
136                if i < widths.len() {
137                    widths[i] = widths[i].max(cell.len());
138                }
139            }
140        }
141
142        // Format headers
143        for (i, header) in self.headers.iter().enumerate() {
144            if i > 0 {
145                output.push_str("  ");
146            }
147            output.push_str(&format!(
148                "{:<width$}",
149                header,
150                width = widths.get(i).unwrap_or(&0)
151            ));
152        }
153        output.push('\n');
154
155        // Add separator
156        for (i, width) in widths.iter().enumerate() {
157            if i > 0 {
158                output.push_str("  ");
159            }
160            output.push_str(&"-".repeat(*width));
161        }
162        output.push('\n');
163
164        // Format rows
165        for row in &self.rows {
166            for (i, cell) in row.iter().enumerate() {
167                if i > 0 {
168                    output.push_str("  ");
169                }
170                output.push_str(&format!(
171                    "{:<width$}",
172                    cell,
173                    width = widths.get(i).unwrap_or(&0)
174                ));
175            }
176            output.push('\n');
177        }
178
179        output
180    }
181}
182
183/// Progress indicator for long-running operations
184pub struct ProgressIndicator {
185    message: String,
186    current: usize,
187    total: Option<usize>,
188}
189
190impl ProgressIndicator {
191    pub fn new(message: String) -> Self {
192        Self {
193            message,
194            current: 0,
195            total: None,
196        }
197    }
198
199    pub fn with_total(message: String, total: usize) -> Self {
200        Self {
201            message,
202            current: 0,
203            total: Some(total),
204        }
205    }
206
207    pub fn increment(&mut self) {
208        self.current += 1;
209        self.display();
210    }
211
212    pub fn set_current(&mut self, current: usize) {
213        self.current = current;
214        self.display();
215    }
216
217    pub fn finish(&self) {
218        println!("{} ✅ Done", self.message);
219    }
220
221    fn display(&self) {
222        if let Some(total) = self.total {
223            let percentage = (self.current as f64 / total as f64 * 100.0) as usize;
224            print!(
225                "\r{} [{}/{}] {}%",
226                self.message, self.current, total, percentage
227            );
228        } else {
229            print!("\r{} [{}]", self.message, self.current);
230        }
231        // Flush stdout to ensure immediate display
232        use std::io::{self, Write};
233        let _ = io::stdout().flush();
234    }
235}