Skip to main content

check_updates_cli/
lib.rs

1use check_updates::CheckUpdates;
2use console::Style;
3use indicatif::{ProgressBar, ProgressStyle};
4
5pub mod cli;
6mod interactive;
7mod update;
8mod version;
9
10pub fn run(args: cli::Args) {
11    env_logger::Builder::new()
12        .filter_level(if args.verbose {
13            log::LevelFilter::Debug
14        } else {
15            log::LevelFilter::Info
16        })
17        .init();
18
19    let strategy = version::VersionStrategy::from_args(&args);
20
21    let spinner = ProgressBar::new_spinner().with_style(
22        ProgressStyle::default_spinner()
23            .template("{spinner:.cyan} {msg}")
24            .expect("valid template"),
25    );
26    spinner.set_message("Fetching package data...");
27    spinner.enable_steady_tick(std::time::Duration::from_millis(80));
28
29    let check_updates = CheckUpdates::new(args.root.clone());
30    let packages = match check_updates.packages() {
31        Ok(p) => p,
32        Err(e) => {
33            spinner.finish_and_clear();
34            eprintln!("{} {e}", Style::new().red().bold().apply_to("error:"));
35            std::process::exit(1);
36        }
37    };
38
39    spinner.finish_and_clear();
40
41    if !args.package.is_empty() {
42        let known: std::collections::HashSet<&str> = packages
43            .values()
44            .flatten()
45            .map(|(_, _, pkg)| pkg.purl.name())
46            .collect();
47
48        for name in &args.package {
49            if !known.contains(name.as_str()) {
50                eprintln!(
51                    "{} package '{}' not found in dependencies",
52                    Style::new().red().bold().apply_to("error:"),
53                    name
54                );
55                std::process::exit(1);
56            }
57        }
58    }
59
60    let updates = update::resolve_updates(&packages, &strategy, &args.package);
61
62    if args.interactive {
63        if updates.is_empty() {
64            update::print_summary(&updates);
65            return;
66        }
67
68        let selected = match interactive::prompt_updates(&updates, args.compact) {
69            Ok(s) => s,
70            Err(e) => {
71                eprintln!("{} {e}", Style::new().red().bold().apply_to("error:"));
72                std::process::exit(1);
73            }
74        };
75
76        if selected.is_empty() {
77            println!("\n No packages selected.");
78            return;
79        }
80
81        let count = selected.len();
82        if let Err(e) =
83            check_updates.update_versions(selected.iter().map(|(u, p, r)| (*u, *p, r.clone())))
84        {
85            eprintln!("{} {e}", Style::new().red().bold().apply_to("error:"));
86            std::process::exit(1);
87        }
88
89        if args.update {
90            run_cargo_update();
91        }
92
93        println!(
94            "\n Upgraded {count} {}.",
95            if count == 1 {
96                "dependency"
97            } else {
98                "dependencies"
99            }
100        );
101    } else if args.upgrade || args.update {
102        update::print_summary(&updates);
103
104        if updates.is_empty() {
105            if args.update {
106                run_cargo_update();
107            }
108            return;
109        }
110
111        let count: usize = updates.values().map(|v| v.len()).sum();
112
113        if let Err(e) = check_updates.update_versions(updates.values().flat_map(|unit_updates| {
114            unit_updates
115                .iter()
116                .map(|u| (u.usage, u.package, u.new_req.clone()))
117        })) {
118            eprintln!("{} {e}", Style::new().red().bold().apply_to("error:"));
119            std::process::exit(1);
120        }
121
122        if args.update {
123            run_cargo_update();
124        }
125
126        println!(
127            "\n Upgraded {count} {}.",
128            if count == 1 {
129                "dependency"
130            } else {
131                "dependencies"
132            }
133        );
134    } else {
135        update::print_summary(&updates);
136        if !updates.is_empty() {
137            println!(
138                "\n{}",
139                Style::new().dim().apply_to("Run with -u or -U to upgrade.")
140            );
141        }
142    }
143}
144
145fn run_cargo_update() {
146    let status = std::process::Command::new("cargo")
147        .arg("update")
148        .status()
149        .expect("failed to run cargo update");
150
151    if !status.success() {
152        eprintln!(
153            "{} cargo update failed",
154            Style::new().red().bold().apply_to("error:")
155        );
156        std::process::exit(1);
157    }
158}