this repo has no description

feat: sort by size

closes #13

+151 -46
+19
Cargo.lock
··· 238 "nu-ansi-term", 239 "serde", 240 "serde_json", 241 ] 242 243 [[package]] ··· 380 ] 381 382 [[package]] 383 name = "strsim" 384 version = "0.11.1" 385 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 463 version = "0.1.1" 464 source = "registry+https://github.com/rust-lang/crates.io-index" 465 checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" 466 467 [[package]] 468 name = "windows-link"
··· 238 "nu-ansi-term", 239 "serde", 240 "serde_json", 241 + "strip-ansi-escapes", 242 ] 243 244 [[package]] ··· 381 ] 382 383 [[package]] 384 + name = "strip-ansi-escapes" 385 + version = "0.2.1" 386 + source = "registry+https://github.com/rust-lang/crates.io-index" 387 + checksum = "2a8f8038e7e7969abb3f1b7c2a811225e9296da208539e0f79c5251d6cac0025" 388 + dependencies = [ 389 + "vte", 390 + ] 391 + 392 + [[package]] 393 name = "strsim" 394 version = "0.11.1" 395 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 473 version = "0.1.1" 474 source = "registry+https://github.com/rust-lang/crates.io-index" 475 checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" 476 + 477 + [[package]] 478 + name = "vte" 479 + version = "0.14.1" 480 + source = "registry+https://github.com/rust-lang/crates.io-index" 481 + checksum = "231fdcd7ef3037e8330d8e17e61011a2c244126acc0a982f4040ac3f9f0bc077" 482 + dependencies = [ 483 + "memchr", 484 + ] 485 486 [[package]] 487 name = "windows-link"
+1
Cargo.toml
··· 14 nu-ansi-term = "0.50.1" 15 serde = { version = "1.0.223", features = ["derive"] } 16 serde_json = "1.0.145" 17 18 [lints.clippy] 19 all = "warn"
··· 14 nu-ansi-term = "0.50.1" 15 serde = { version = "1.0.223", features = ["derive"] } 16 serde_json = "1.0.145" 17 + strip-ansi-escapes = "0.2.1" 18 19 [lints.clippy] 20 all = "warn"
+102 -42
src/diff.rs
··· 1 - use humansize::{format_size, DECIMAL}; 2 use nu_ansi_term::Color::{self, Green, Red, Yellow}; 3 use std::collections::BTreeMap; 4 5 use super::{ 6 - package::{DiffType, Package}, 7 parser::DiffRoot, 8 }; 9 10 #[derive(Debug)] 11 pub struct PackageListDiff { 12 - pub added: BTreeMap<String, Package>, 13 - pub removed: BTreeMap<String, Package>, 14 - pub changed: BTreeMap<String, Package>, 15 - size_delta: i64, 16 longest_name: usize, 17 } 18 19 - impl From<DiffRoot> for PackageListDiff { 20 - fn from(diff: DiffRoot) -> Self { 21 - let mut out = PackageListDiff { 22 added: BTreeMap::new(), 23 removed: BTreeMap::new(), 24 changed: BTreeMap::new(), 25 - size_delta: 0, 26 longest_name: 0, 27 - }; 28 29 - for (name, diff_package) in diff.packages { 30 - let package = Package::from(diff_package); 31 32 - out.size_delta += package.size_delta; 33 - out.longest_name = out.longest_name.max(name.len()); 34 35 - match package.diff_type { 36 - DiffType::Added => { 37 - out.added.insert(name, package); 38 - } 39 - DiffType::Removed => { 40 - out.removed.insert(name, package); 41 - } 42 - DiffType::Changed => { 43 - out.changed.insert(name, package); 44 - } 45 - DiffType::Unknown => { 46 - // This should never happen, but just in case 47 - eprintln!("Unknown diff type for package: {name}"); 48 - } 49 - } 50 } 51 52 - out 53 } 54 } 55 56 - impl std::fmt::Display for PackageListDiff { 57 - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 58 - if self.added.is_empty() && self.removed.is_empty() && self.changed.is_empty() { 59 - return write!(f, "No differences found."); 60 } 61 62 let title_style = nu_ansi_term::Style::new() 63 .underline() 64 .bold() ··· 91 writeln!(f)?; 92 } 93 94 - { 95 - let delta = self.size_delta; 96 - let sign = if delta > 0 { "+" } else { "-" }; 97 - let size: u64 = delta.abs().try_into().unwrap_or(0); 98 - writeln!(f, "size diff: {sign}{}", format_size(size, DECIMAL))?; 99 - } 100 101 - Ok(()) 102 } 103 }
··· 1 use nu_ansi_term::Color::{self, Green, Red, Yellow}; 2 use std::collections::BTreeMap; 3 4 use super::{ 5 + package::{DiffType, Package, SizeDelta}, 6 parser::DiffRoot, 7 }; 8 + 9 + #[derive(Debug)] 10 + pub struct PackageExtra { 11 + name: String, 12 + base_package: Package, 13 + } 14 15 #[derive(Debug)] 16 pub struct PackageListDiff { 17 + all: Vec<PackageExtra>, 18 + added: BTreeMap<String, Package>, 19 + removed: BTreeMap<String, Package>, 20 + changed: BTreeMap<String, Package>, 21 + size_delta: SizeDelta, 22 longest_name: usize, 23 + 24 + // Whether to sort by size difference when displaying 25 + pub by_size: bool, 26 } 27 28 + impl PackageListDiff { 29 + pub fn new() -> Self { 30 + PackageListDiff { 31 + all: Vec::new(), 32 added: BTreeMap::new(), 33 removed: BTreeMap::new(), 34 changed: BTreeMap::new(), 35 + size_delta: SizeDelta(0), 36 longest_name: 0, 37 + by_size: false, 38 + } 39 + } 40 + } 41 42 + impl std::fmt::Display for PackageListDiff { 43 + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 44 + if self.by_size { 45 + self.display_by_size(f)?; 46 + } else { 47 + if self.added.is_empty() && self.removed.is_empty() && self.changed.is_empty() { 48 + return write!(f, "No differences found."); 49 + } 50 51 + self.display_by_category(f)?; 52 + } 53 54 + { 55 + let delta = &self.size_delta; 56 + writeln!(f, "size diff: {delta}")?; 57 } 58 59 + Ok(()) 60 } 61 } 62 63 + impl PackageListDiff { 64 + fn display_by_size(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 65 + let all = { 66 + let mut v: Vec<_> = self.all.iter().collect(); 67 + v.sort_by_key(|pkg| -pkg.base_package.size_delta.0); 68 + v 69 + }; 70 + 71 + let name_width = self.longest_name + 2; 72 + let package_width = (all 73 + .iter() 74 + .map(|pkg| strip_ansi_escapes::strip(format!("{}", pkg.base_package)).len()) 75 + .max() 76 + .expect("At least one package exists")) 77 + .min(120); 78 + 79 + for package in &all { 80 + let name = &package.name; 81 + let package = &package.base_package; 82 + let delta = &package.size_delta; 83 + let versions = format!("{package}"); 84 + let visual_len = strip_ansi_escapes::strip(&versions).len(); 85 + let padding = " ".repeat(package_width.saturating_sub(visual_len)); 86 + writeln!(f, "{name:name_width$}{versions}{padding} {delta}")?; 87 } 88 89 + writeln!(f)?; 90 + 91 + Ok(()) 92 + } 93 + 94 + fn display_by_category(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 95 let title_style = nu_ansi_term::Style::new() 96 .underline() 97 .bold() ··· 124 writeln!(f)?; 125 } 126 127 + Ok(()) 128 + } 129 + 130 + pub fn from_diff_root(&mut self, diff_root: DiffRoot) { 131 + for (name, diff_package) in diff_root.packages { 132 + let package = Package::from(diff_package); 133 134 + self.size_delta.0 += package.size_delta.0; 135 + self.longest_name = self.longest_name.max(name.len()); 136 + 137 + if self.by_size { 138 + self.all.push(PackageExtra { 139 + name, 140 + base_package: package, 141 + }); 142 + 143 + continue; 144 + } 145 + 146 + match package.diff_type { 147 + DiffType::Added => { 148 + self.added.insert(name, package); 149 + } 150 + DiffType::Removed => { 151 + self.removed.insert(name, package); 152 + } 153 + DiffType::Changed => { 154 + self.changed.insert(name, package); 155 + } 156 + DiffType::Unknown => { 157 + // This should never happen, but just in case 158 + eprintln!("Unknown diff type for package: {name}"); 159 + } 160 + } 161 + } 162 } 163 }
+8 -1
src/main.rs
··· 25 /// the generation we are switching to 26 #[arg(default_value = "/run/current-system/")] 27 after: PathBuf, 28 } 29 30 fn main() -> Result<()> { ··· 52 std::process::exit(1); 53 } 54 55 - let packages: PackageListDiff = DiffRoot::new(lix_exe, &before, &after)?.into(); 56 57 let arrow_style = Style::new().bold().fg(Color::LightGray); 58
··· 25 /// the generation we are switching to 26 #[arg(default_value = "/run/current-system/")] 27 after: PathBuf, 28 + 29 + /// sort by size difference 30 + #[arg(short, long)] 31 + size: bool, 32 } 33 34 fn main() -> Result<()> { ··· 56 std::process::exit(1); 57 } 58 59 + let packages_diff = DiffRoot::new(lix_exe, &before, &after)?; 60 + let mut packages: PackageListDiff = PackageListDiff::new(); 61 + packages.by_size = args.size; 62 + packages.from_diff_root(packages_diff); 63 64 let arrow_style = Style::new().bold().fg(Color::LightGray); 65
+20 -2
src/package.rs
··· 1 use std::{cmp::Ordering, fmt::Display}; 2 3 use super::{ ··· 17 18 #[derive(Debug)] 19 pub struct Package { 20 - pub size_delta: i64, 21 pub diff_type: DiffType, 22 23 pub versions_before: VersionList, 24 pub versions_after: VersionList, 25 } 26 27 impl DiffType { 28 pub fn from_versions(before: &[String], after: &[String]) -> DiffType { ··· 66 }; 67 68 Package { 69 - size_delta: diff.size_delta, 70 versions_before: parsed_before, 71 versions_after: parsed_after, 72 diff_type, ··· 133 134 (parsed_before, parsed_after) 135 }
··· 1 + use humansize::{format_size, DECIMAL}; 2 use std::{cmp::Ordering, fmt::Display}; 3 4 use super::{ ··· 18 19 #[derive(Debug)] 20 pub struct Package { 21 + pub size_delta: SizeDelta, 22 pub diff_type: DiffType, 23 24 pub versions_before: VersionList, 25 pub versions_after: VersionList, 26 } 27 + 28 + #[derive(Debug)] 29 + pub struct SizeDelta(pub i64); 30 31 impl DiffType { 32 pub fn from_versions(before: &[String], after: &[String]) -> DiffType { ··· 70 }; 71 72 Package { 73 + size_delta: diff.size_delta.into(), 74 versions_before: parsed_before, 75 versions_after: parsed_after, 76 diff_type, ··· 137 138 (parsed_before, parsed_after) 139 } 140 + 141 + impl std::fmt::Display for SizeDelta { 142 + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 143 + let sign = if self.0 >= 0 { "+" } else { "-" }; 144 + let size: u64 = self.0.abs().try_into().unwrap_or(0); 145 + write!(f, "{sign}{}", format_size(size, DECIMAL)) 146 + } 147 + } 148 + 149 + impl From<i64> for SizeDelta { 150 + fn from(size: i64) -> Self { 151 + SizeDelta(size) 152 + } 153 + }
+1 -1
src/parser.rs
··· 52 lix_exe = lix_path; 53 } else { 54 lix_exe = "nix".into(); 55 - }; 56 57 let raw_diff = Command::new(lix_exe) 58 .args(["store", "diff-closures", "--json"])
··· 52 lix_exe = lix_path; 53 } else { 54 lix_exe = "nix".into(); 55 + } 56 57 let raw_diff = Command::new(lix_exe) 58 .args(["store", "diff-closures", "--json"])