this repo has no description
1use nu_ansi_term::Color::{self, Green, Red, Yellow};
2use std::collections::BTreeMap;
3use terminal_light::luma;
4
5use super::{
6 color,
7 package::{DiffType, Package, SizeDelta},
8 parser::DiffRoot,
9};
10
11#[derive(Debug)]
12pub struct PackageExtra {
13 name: String,
14 base_package: Package,
15}
16
17#[derive(Debug)]
18pub struct PackageListDiff {
19 all: Vec<PackageExtra>,
20 added: BTreeMap<String, Package>,
21 removed: BTreeMap<String, Package>,
22 changed: BTreeMap<String, Package>,
23 size_delta: SizeDelta,
24 longest_name: usize,
25
26 // Whether to sort by size difference when displaying
27 pub by_size: bool,
28}
29
30impl PackageListDiff {
31 pub fn new() -> Self {
32 PackageListDiff {
33 all: Vec::new(),
34 added: BTreeMap::new(),
35 removed: BTreeMap::new(),
36 changed: BTreeMap::new(),
37 size_delta: SizeDelta(0),
38 longest_name: 0,
39 by_size: false,
40 }
41 }
42}
43
44impl std::fmt::Display for PackageListDiff {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 if self.by_size {
47 self.display_by_size(f)?;
48 } else {
49 if self.added.is_empty() && self.removed.is_empty() && self.changed.is_empty() {
50 return write!(f, "No differences found.");
51 }
52
53 self.display_by_category(f)?;
54 }
55
56 {
57 let delta = &self.size_delta;
58 writeln!(f, "size diff: {delta}")?;
59 }
60
61 Ok(())
62 }
63}
64
65impl PackageListDiff {
66 fn display_by_size(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 let all = {
68 let mut v: Vec<_> = self.all.iter().collect();
69 v.sort_by_key(|pkg| -pkg.base_package.size_delta.0);
70 v
71 };
72
73 let name_width = self.longest_name + 2;
74 let package_width = (all
75 .iter()
76 .map(|pkg| strip_ansi_escapes::strip(format!("{}", pkg.base_package)).len())
77 .max()
78 .expect("At least one package exists"))
79 .min(120);
80
81 for package in &all {
82 let name = &package.name;
83 let package = &package.base_package;
84 let delta = &package.size_delta;
85 let versions = format!("{package}");
86 let visual_len = strip_ansi_escapes::strip(&versions).len();
87 let padding = " ".repeat(package_width.saturating_sub(visual_len));
88 writeln!(f, "{name:name_width$}{versions}{padding} {delta}")?;
89 }
90
91 writeln!(f)?;
92
93 Ok(())
94 }
95
96 fn display_by_category(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 let name_width = self.longest_name + 2;
98
99 if color::color_enabled() {
100 let text_color = if luma().is_ok_and(|luma| luma > 0.6) {
101 Color::DarkGray
102 } else {
103 Color::LightGray
104 };
105 let title_style = nu_ansi_term::Style::new().underline().bold().fg(text_color);
106
107 if !self.changed.is_empty() {
108 writeln!(f, "{}", &title_style.paint("Changed"))?;
109 for (name, package) in &self.changed {
110 writeln!(f, "[{}] {name:name_width$}{package}", Yellow.paint("C"))?;
111 }
112 writeln!(f)?;
113 }
114
115 if !self.added.is_empty() {
116 writeln!(f, "{}", &title_style.paint("Added"))?;
117 for (name, package) in &self.added {
118 write!(f, "[{}] {name:name_width$}{package}", Green.paint("A"))?;
119 writeln!(f)?;
120 }
121 writeln!(f)?;
122 }
123
124 if !self.removed.is_empty() {
125 writeln!(f, "{}", &title_style.paint("Removed"))?;
126 for (name, package) in &self.removed {
127 writeln!(f, "[{}] {name:name_width$}{package}", Red.paint("R"))?;
128 }
129 writeln!(f)?;
130 }
131 } else {
132 if !self.changed.is_empty() {
133 writeln!(f, "Changed")?;
134 for (name, package) in &self.changed {
135 writeln!(f, "[C] {name:name_width$}{package}")?;
136 }
137 writeln!(f)?;
138 }
139
140 if !self.added.is_empty() {
141 writeln!(f, "Added")?;
142 for (name, package) in &self.added {
143 writeln!(f, "[A] {name:name_width$}{package}")?;
144 }
145 writeln!(f)?;
146 }
147
148 if !self.removed.is_empty() {
149 writeln!(f, "Removed")?;
150 for (name, package) in &self.removed {
151 writeln!(f, "[R] {name:name_width$}{package}")?;
152 }
153 writeln!(f)?;
154 }
155 }
156
157 Ok(())
158 }
159
160 #[allow(clippy::wrong_self_convention)]
161 pub fn from_diff_root(&mut self, diff_root: DiffRoot) {
162 for (name, diff_package) in diff_root.packages {
163 let package = Package::from(diff_package);
164
165 self.size_delta.0 += package.size_delta.0;
166 self.longest_name = self.longest_name.max(name.len());
167
168 if self.by_size {
169 self.all.push(PackageExtra {
170 name,
171 base_package: package,
172 });
173
174 continue;
175 }
176
177 match package.diff_type {
178 DiffType::Added => {
179 self.added.insert(name, package);
180 }
181 DiffType::Removed => {
182 self.removed.insert(name, package);
183 }
184 DiffType::Changed => {
185 self.changed.insert(name, package);
186 }
187 DiffType::Unknown => {
188 // This should never happen, but just in case
189 eprintln!("Unknown diff type for package: {name}");
190 }
191 }
192 }
193 }
194}