tidy/
x_version.rs

1use std::path::Path;
2use std::process::{Command, Stdio};
3
4use semver::Version;
5
6pub fn check(root: &Path, cargo: &Path, bad: &mut bool) {
7    let cargo_list = Command::new(cargo).args(["install", "--list"]).stdout(Stdio::piped()).spawn();
8
9    let child = match cargo_list {
10        Ok(child) => child,
11        Err(e) => return tidy_error!(bad, "failed to run `cargo`: {}", e),
12    };
13
14    let cargo_list = child.wait_with_output().unwrap();
15
16    if cargo_list.status.success() {
17        let exe_list = String::from_utf8_lossy(&cargo_list.stdout);
18        let exe_list = exe_list.lines();
19
20        let mut installed: Option<Version> = None;
21
22        for line in exe_list {
23            let mut iter = line.split_whitespace();
24            if iter.next() == Some("x") {
25                if let Some(version) = iter.next() {
26                    // Check this is the rust-lang/rust x tool installation since it should be
27                    // installed at a path containing `src/tools/x`.
28                    if let Some(path) = iter.next() {
29                        if path.contains(&"src/tools/x") {
30                            let version = version.strip_prefix("v").unwrap();
31                            installed = Some(Version::parse(version).unwrap());
32                            break;
33                        }
34                    };
35                }
36            } else {
37                continue;
38            }
39        }
40        // Unwrap the some if x is installed, otherwise return because it's fine if x isn't installed.
41        let installed = if let Some(i) = installed { i } else { return };
42
43        if let Some(expected) = get_x_wrapper_version(root, cargo) {
44            if installed < expected {
45                return println!(
46                    "Current version of x is {installed}, but the latest version is {expected}\nConsider updating to the newer version of x by running `cargo install --path src/tools/x`"
47                );
48            }
49        } else {
50            return tidy_error!(
51                bad,
52                "Unable to parse the latest version of `x` at `src/tools/x/Cargo.toml`"
53            );
54        }
55    } else {
56        tidy_error!(bad, "failed to check version of `x`: {}", cargo_list.status)
57    }
58}
59
60// Parse latest version out of `x` Cargo.toml
61fn get_x_wrapper_version(root: &Path, cargo: &Path) -> Option<Version> {
62    let mut cmd = cargo_metadata::MetadataCommand::new();
63    cmd.cargo_path(cargo)
64        .manifest_path(root.join("src/tools/x/Cargo.toml"))
65        .no_deps()
66        .features(cargo_metadata::CargoOpt::AllFeatures);
67    let mut metadata = t!(cmd.exec());
68    metadata.packages.pop().map(|x| x.version)
69}