1use anyhow::{Context, Result};
2use colored::*;
3use std::fmt::Display;
4use std::fs;
5use std::path::Path;
6
7pub struct Io {
9 verbose: bool,
10}
11
12impl Io {
13 pub fn new(verbose: bool) -> Self {
15 Self { verbose }
16 }
17
18 pub fn header<D: Display>(&self, text: D) {
20 println!("\n{}", text.to_string().bold().bright_cyan());
21 }
22
23 pub fn success<D: Display>(&self, text: D) {
25 println!("{} {}", "✔".green(), text);
26 }
27
28 pub fn error<D: Display>(&self, text: D) {
30 eprintln!("{} {}", "✘".red(), text);
31 }
32
33 pub fn warning<D: Display>(&self, text: D) {
35 println!("{} {}", "!".yellow(), text);
36 }
37
38 pub fn info<D: Display>(&self, text: D) {
40 if self.verbose {
41 println!("{} {}", "ℹ".blue(), text);
42 }
43 }
44
45 pub fn read_file<P: AsRef<Path>>(&self, path: P) -> Result<String> {
47 let path = path.as_ref();
48 self.info(format!("Reading file: {}", path.display()));
49 fs::read_to_string(path).with_context(|| format!("Failed to read file: {}", path.display()))
50 }
51
52 pub fn write_file<P: AsRef<Path>>(&self, path: P, content: &str) -> Result<()> {
54 let path = path.as_ref();
55 self.info(format!("Writing file: {}", path.display()));
56 if let Some(parent) = path.parent() {
57 if !parent.exists() {
58 fs::create_dir_all(parent)
59 .with_context(|| format!("Failed to create directory: {}", parent.display()))?;
60 }
61 }
62 fs::write(path, content)
63 .with_context(|| format!("Failed to write file: {}", path.display()))
64 }
65}
66
67pub struct Table {
69 headers: Vec<String>,
70 rows: Vec<Vec<String>>,
71}
72
73impl Table {
74 pub fn new(headers: Vec<&str>) -> Self {
76 Self {
77 headers: headers.into_iter().map(|s| s.to_string()).collect(),
78 rows: Vec::new(),
79 }
80 }
81
82 pub fn add_row(&mut self, row: Vec<String>) {
84 self.rows.push(row);
85 }
86
87 pub fn print(&self) {
89 if self.headers.is_empty() && self.rows.is_empty() {
90 return;
91 }
92
93 let mut col_widths = self.headers.iter().map(|h| h.len()).collect::<Vec<_>>();
94 for row in &self.rows {
95 for (i, cell) in row.iter().enumerate() {
96 if i < col_widths.len() {
97 col_widths[i] = col_widths[i].max(cell.len());
98 }
99 }
100 }
101
102 println!();
104 for (i, header) in self.headers.iter().enumerate() {
105 print!(
106 "{:<width$} ",
107 header.bold().underline(),
108 width = col_widths[i]
109 );
110 }
111 println!();
112
113 for row in &self.rows {
115 for (i, cell) in row.iter().enumerate() {
116 if i < col_widths.len() {
117 print!("{:<width$} ", cell, width = col_widths[i]);
118 }
119 }
120 println!();
121 }
122 println!();
123 }
124}