1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
use semver::Version;
use std::path::Path;
use std::process::{Command, Stdio};

pub fn check(root: &Path, cargo: &Path, bad: &mut bool) {
    let cargo_list = Command::new(cargo).args(["install", "--list"]).stdout(Stdio::piped()).spawn();

    let child = match cargo_list {
        Ok(child) => child,
        Err(e) => return tidy_error!(bad, "failed to run `cargo`: {}", e),
    };

    let cargo_list = child.wait_with_output().unwrap();

    if cargo_list.status.success() {
        let exe_list = String::from_utf8_lossy(&cargo_list.stdout);
        let exe_list = exe_list.lines();

        let mut installed: Option<Version> = None;

        for line in exe_list {
            let mut iter = line.split_whitespace();
            if iter.next() == Some("x") {
                if let Some(version) = iter.next() {
                    // Check this is the rust-lang/rust x tool installation since it should be
                    // installed at a path containing `src/tools/x`.
                    if let Some(path) = iter.next() {
                        if path.contains(&"src/tools/x") {
                            let version = version.strip_prefix("v").unwrap();
                            installed = Some(Version::parse(version).unwrap());
                            break;
                        }
                    };
                }
            } else {
                continue;
            }
        }
        // Unwrap the some if x is installed, otherwise return because it's fine if x isn't installed.
        let installed = if let Some(i) = installed { i } else { return };

        if let Some(expected) = get_x_wrapper_version(root, cargo) {
            if installed < expected {
                return println!(
                    "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`"
                );
            }
        } else {
            return tidy_error!(
                bad,
                "Unable to parse the latest version of `x` at `src/tools/x/Cargo.toml`"
            );
        }
    } else {
        return tidy_error!(bad, "failed to check version of `x`: {}", cargo_list.status);
    }
}

// Parse latest version out of `x` Cargo.toml
fn get_x_wrapper_version(root: &Path, cargo: &Path) -> Option<Version> {
    let mut cmd = cargo_metadata::MetadataCommand::new();
    cmd.cargo_path(cargo)
        .manifest_path(root.join("src/tools/x/Cargo.toml"))
        .no_deps()
        .features(cargo_metadata::CargoOpt::AllFeatures);
    let mut metadata = t!(cmd.exec());
    metadata.packages.pop().map(|x| x.version)
}