Skip to main content

check_updates_core/
output.rs

1use crate::types::{DependencyCheck, UpdateSeverity};
2use colored::Colorize;
3
4/// Renders the dependency check results in a table format
5pub struct TableRenderer {
6    show_colors: bool,
7}
8
9impl TableRenderer {
10    pub fn new(show_colors: bool) -> Self {
11        Self { show_colors }
12    }
13
14    /// Render all packages with updates
15    pub fn render(&self, checks: &[DependencyCheck]) {
16        let checks_with_updates: Vec<&DependencyCheck> = checks
17            .iter()
18            .filter(|check| check.has_update())
19            .collect();
20
21        self.render_deduped(&checks_with_updates);
22    }
23
24    /// Render a deduplicated list of checks
25    pub fn render_deduped(&self, checks: &[&DependencyCheck]) {
26        if checks.is_empty() {
27            println!("All dependencies are up to date!");
28            return;
29        }
30
31        // Calculate column widths
32        let max_name = checks
33            .iter()
34            .map(|c| c.dependency.name.len())
35            .max()
36            .unwrap_or(0);
37
38        let max_from = checks
39            .iter()
40            .filter_map(|c| c.current_version())
41            .map(|v| v.to_string().len())
42            .max()
43            .unwrap_or(0);
44
45        let max_to = checks
46            .iter()
47            .filter_map(|c| c.target.as_ref())
48            .map(|v| v.to_string().len())
49            .max()
50            .unwrap_or(0);
51
52        println!("Outdated dependencies:\n");
53
54        for check in checks {
55            self.print_row(check, max_name, max_from, max_to);
56        }
57    }
58
59    fn print_row(
60        &self,
61        check: &DependencyCheck,
62        name_width: usize,
63        from_width: usize,
64        to_width: usize,
65    ) {
66        let from = check
67            .current_version()
68            .map(std::string::ToString::to_string)
69            .unwrap_or_default();
70
71        let to = check
72            .target
73            .as_ref()
74            .map(std::string::ToString::to_string)
75            .unwrap_or_default();
76
77        let severity_str = self.format_severity(check.severity);
78
79        let available_hint = if check.has_newer_available() {
80            format!("  ({} available)", check.latest)
81        } else {
82            String::new()
83        };
84
85        println!(
86            "  {:<name_w$}  {:>from_w$} → {:<to_w$}  {}{}",
87            check.dependency.name,
88            from,
89            to,
90            severity_str,
91            available_hint,
92            name_w = name_width,
93            from_w = from_width,
94            to_w = to_width,
95        );
96    }
97
98    /// Format severity with optional colors
99    pub fn format_severity(&self, severity: Option<UpdateSeverity>) -> String {
100        match severity {
101            Some(UpdateSeverity::Major) => {
102                if self.show_colors {
103                    "MAJOR".red().to_string()
104                } else {
105                    "MAJOR".to_string()
106                }
107            }
108            Some(UpdateSeverity::Minor) => {
109                if self.show_colors {
110                    "minor".yellow().to_string()
111                } else {
112                    "minor".to_string()
113                }
114            }
115            Some(UpdateSeverity::Patch) => {
116                if self.show_colors {
117                    "patch".green().to_string()
118                } else {
119                    "patch".to_string()
120                }
121            }
122            None => String::new(),
123        }
124    }
125}