1#[cfg(test)]
7#[path = "toolchain_test.rs"]
8mod toolchain_test;
9
10use crate::environment::expand_value;
11use crate::types::{CommandSpec, ToolchainSpecifier};
12use semver::{Prerelease, Version};
13use std::process::{Command, Stdio};
14
15pub(crate) fn get_channel(toolchain: &ToolchainSpecifier) -> String {
16 let channel = toolchain.channel().to_string();
17 expand_value(&channel)
18}
19
20pub(crate) fn wrap_command(
21 toolchain: &ToolchainSpecifier,
22 command: &str,
23 args: &Option<Vec<String>>,
24) -> CommandSpec {
25 check_toolchain(toolchain);
26
27 let channel = get_channel(&toolchain);
28 if channel.is_empty() {
29 let mut command_args = vec![];
30 if let Some(array) = args {
31 for arg in array.iter() {
32 command_args.push(arg.to_string());
33 }
34 };
35
36 CommandSpec {
37 command: command.to_string(),
38 args: Some(command_args),
39 }
40 } else {
41 let mut rustup_args = vec!["run".to_string(), channel, command.to_string()];
42 if let Some(array) = args {
43 for arg in array.iter() {
44 rustup_args.push(arg.to_string());
45 }
46 };
47
48 CommandSpec {
49 command: "rustup".to_string(),
50 args: Some(rustup_args),
51 }
52 }
53}
54
55fn get_specified_min_version(toolchain: &ToolchainSpecifier) -> Option<Version> {
56 let min_version = toolchain.min_version()?;
57 let spec_min_version = min_version.parse::<Version>();
58 if let Err(_) = spec_min_version {
59 warn!("Unable to parse min version value: {}", &min_version);
60 }
61 spec_min_version.ok()
62}
63
64fn check_toolchain(toolchain: &ToolchainSpecifier) {
65 let channel = get_channel(&toolchain);
66
67 if channel.is_empty() {
68 return;
69 }
70
71 let output = Command::new("rustup")
72 .args(&["run", &channel, "rustc", "--version"])
73 .stderr(Stdio::null())
74 .stdout(Stdio::piped())
75 .output()
76 .expect("Failed to check rustup toolchain");
77 if !output.status.success() {
78 error!(
79 "Missing toolchain {}! Please install it using rustup.",
80 &channel
81 );
82 return;
83 }
84
85 let spec_min_version = get_specified_min_version(toolchain);
86 if let Some(ref spec_min_version) = spec_min_version {
87 let rustc_version = String::from_utf8_lossy(&output.stdout);
88 let rustc_version = rustc_version
89 .split(" ")
90 .nth(1)
91 .expect("expected a version in rustc output");
92 let mut rustc_version = rustc_version
93 .parse::<Version>()
94 .expect("unexpected version format");
95 rustc_version.pre = Prerelease::EMPTY;
103
104 if &rustc_version < spec_min_version {
105 error!(
106 "Installed toolchain {} is required to satisfy version {}, found {}! Please upgrade it using rustup.",
107 &channel,
108 &spec_min_version,
109 rustc_version,
110 );
111 }
112 }
113}
114
115pub(crate) fn get_cargo_binary_path(toolchain: &ToolchainSpecifier) -> Option<String> {
116 let command_spec = wrap_command(
117 toolchain,
118 "rustup",
119 &Some(vec!["which".to_string(), "cargo".to_string()]),
120 );
121 let mut command = Command::new(&command_spec.command);
122 match command_spec.args {
123 Some(ref args_vec) => {
124 command.args(args_vec);
125 }
126 None => debug!("No command args defined."),
127 };
128
129 let output = command
130 .stderr(Stdio::null())
131 .stdout(Stdio::piped())
132 .output()
133 .expect("Failed to check rustup toolchain");
134 if !output.status.success() {
135 error!(
136 "Missing toolchain {}! Please install it using rustup.",
137 &toolchain
138 );
139 return None;
140 }
141
142 let binary_path = String::from_utf8_lossy(&output.stdout);
143 if binary_path.is_empty() {
144 None
145 } else {
146 Some(binary_path.to_string())
147 }
148}
149
150pub(crate) fn remove_rust_env_vars() {
151 envmnt::remove_all(&vec!["CARGO", "RUSTC", "RUSTDOC"]);
152}