this repo has no description
1use std::path::PathBuf;
2
3use clap::Parser;
4use color_eyre::Result;
5use diff::PackageListDiff;
6use nu_ansi_term::{Color, Style};
7use shellexpand::path::tilde;
8use terminal_light::luma;
9
10mod color;
11mod diff;
12mod package;
13mod parser;
14mod versioning;
15
16use self::parser::DiffRoot;
17
18#[allow(clippy::struct_excessive_bools)]
19#[derive(Parser, PartialEq, Debug)]
20/// List the package differences between two `NixOS` generations
21struct Args {
22 /// the path to the lix bin directory
23 #[arg(short, long)]
24 lix_bin: Option<PathBuf>,
25
26 /// the generation we are switching from
27 before: PathBuf,
28
29 /// the generation we are switching to
30 #[arg(default_value = "/run/current-system/")]
31 after: PathBuf,
32
33 /// sort by size difference
34 #[arg(short, long)]
35 size: bool,
36
37 /// disable colored output (also respects `NO_COLOR` env var and CI environments)
38 #[arg(long)]
39 no_color: bool,
40
41 /// disable the header showing the generation paths
42 #[arg(long)]
43 no_header: bool,
44
45 /// add a return code indicating whether there are changes or no (1 if there are no changes, 0
46 /// otherwise)
47 #[arg(short, long = "return")]
48 return_code: bool,
49}
50
51fn main() -> Result<()> {
52 let args = Args::parse();
53
54 // Initialize color configuration
55 color::init(args.no_color);
56
57 let before = tilde(&args.before);
58 let after = tilde(&args.after);
59 let lix_bin = args.lix_bin.as_deref().map(tilde);
60
61 let mut lix_exe = None;
62 if let Some(lix_bin) = lix_bin {
63 lix_exe = if lix_bin.is_dir() {
64 Some(lix_bin.join("nix").into())
65 } else {
66 Some(lix_bin)
67 }
68 }
69
70 if !before.exists() {
71 eprintln!("Before generation does not exist: {}", before.display());
72 std::process::exit(1);
73 }
74
75 if !after.exists() {
76 eprintln!("After generation does not exist: {}", after.display());
77 std::process::exit(1);
78 }
79
80 let packages_diff = DiffRoot::new(lix_exe.as_deref(), &before, &after)?;
81 let mut packages: PackageListDiff = PackageListDiff::new();
82 packages.by_size = args.size;
83 packages.from_diff_root(packages_diff);
84
85 let before_text = format!("<<< {}", before.display());
86 let after_text = format!(">>> {}", after.display());
87
88 if !args.no_header {
89 if color::color_enabled() {
90 let text_color = if luma().is_ok_and(|luma| luma > 0.6) {
91 Color::DarkGray
92 } else {
93 Color::LightGray
94 };
95 let arrow_style = Style::new().bold().fg(text_color);
96 println!("{}", arrow_style.paint(&before_text));
97 println!("{}\n", arrow_style.paint(&after_text));
98 } else {
99 println!("{before_text}");
100 println!("{after_text}\n");
101 }
102 }
103
104 println!("{packages}");
105
106 if args.return_code {
107 std::process::exit(packages.is_empty().into());
108 }
109
110 Ok(())
111}