Skip to content

Commit ba745f6

Browse files
authored
Prefer stable Rust toolchains (#1974)
Prefer stable Rust toolchains over newer nightly toolchains when resolving system Rust for default requests. Nightly is still used when it is the only matching candidate. Closes #1970
1 parent b6c591d commit ba745f6

3 files changed

Lines changed: 87 additions & 13 deletions

File tree

crates/prek/src/languages/rust/installer.rs

Lines changed: 80 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -116,13 +116,11 @@ impl RustInstaller {
116116
}
117117

118118
async fn find_installed(&self, request: &RustRequest) -> Result<RustResult> {
119-
let toolchains: Vec<ToolchainInfo> = self.rustup.list_installed_toolchains().await?;
119+
let mut toolchains: Vec<ToolchainInfo> = self.rustup.list_installed_toolchains().await?;
120120

121-
let installed = toolchains
122-
.into_iter()
123-
.sorted_unstable_by(|a, b| b.version.cmp(&a.version));
121+
sort_toolchains(&mut toolchains);
124122

125-
installed
123+
toolchains
126124
.into_iter()
127125
.find_map(|info| {
128126
let matches = request.matches(&info.version, Some(&info.path));
@@ -139,13 +137,11 @@ impl RustInstaller {
139137
}
140138

141139
async fn find_system_rust(&self, rust_request: &RustRequest) -> Result<Option<RustResult>> {
142-
let toolchains: Vec<ToolchainInfo> = self.rustup.list_system_toolchains().await?;
140+
let mut toolchains: Vec<ToolchainInfo> = self.rustup.list_system_toolchains().await?;
143141

144-
let installed = toolchains
145-
.into_iter()
146-
.sorted_unstable_by(|a, b| b.version.cmp(&a.version));
142+
sort_toolchains(&mut toolchains);
147143

148-
for info in installed {
144+
for info in toolchains {
149145
let matches = rust_request.matches(&info.version, Some(&info.path));
150146

151147
if matches {
@@ -214,3 +210,77 @@ impl RustInstaller {
214210
Ok(rust)
215211
}
216212
}
213+
214+
fn sort_toolchains(toolchains: &mut [ToolchainInfo]) {
215+
fn channel_preference(version: &RustVersion) -> u8 {
216+
match version.channel() {
217+
Some(Channel::Nightly) => 2,
218+
Some(Channel::Beta) => 1,
219+
// Exact release toolchains do not carry a channel name.
220+
Some(Channel::Stable) | None => 0,
221+
}
222+
}
223+
224+
toolchains.sort_unstable_by(|a, b| {
225+
channel_preference(&a.version)
226+
.cmp(&channel_preference(&b.version))
227+
.then_with(|| b.version.cmp(&a.version))
228+
.then_with(|| a.name.cmp(&b.name))
229+
.then_with(|| a.path.cmp(&b.path))
230+
});
231+
}
232+
233+
#[cfg(test)]
234+
mod tests {
235+
use super::*;
236+
237+
fn toolchain(name: &str, version: &semver::Version, toolchain_name: &str) -> ToolchainInfo {
238+
let path = PathBuf::from("/rustup/toolchains").join(toolchain_name);
239+
ToolchainInfo {
240+
name: name.to_string(),
241+
version: RustVersion::from_path(version, &path),
242+
path,
243+
}
244+
}
245+
246+
#[test]
247+
fn preferred_toolchain_order_prefers_release_over_newer_nightly() {
248+
let mut toolchains = vec![
249+
toolchain(
250+
"nightly-aarch64-apple-darwin",
251+
&semver::Version::new(1, 97, 0),
252+
"nightly-aarch64-apple-darwin",
253+
),
254+
toolchain(
255+
"stable-aarch64-apple-darwin",
256+
&semver::Version::new(1, 90, 0),
257+
"stable-aarch64-apple-darwin",
258+
),
259+
];
260+
261+
sort_toolchains(&mut toolchains);
262+
let selected = toolchains
263+
.into_iter()
264+
.find(|info| RustRequest::Any.matches(&info.version, Some(&info.path)))
265+
.expect("matching toolchain");
266+
267+
assert_eq!(selected.name, "stable-aarch64-apple-darwin");
268+
}
269+
270+
#[test]
271+
fn preferred_toolchain_order_uses_nightly_when_it_is_the_only_candidate() {
272+
let mut toolchains = vec![toolchain(
273+
"nightly-aarch64-apple-darwin",
274+
&semver::Version::new(1, 97, 0),
275+
"nightly-aarch64-apple-darwin",
276+
)];
277+
278+
sort_toolchains(&mut toolchains);
279+
let selected = toolchains
280+
.into_iter()
281+
.find(|info| RustRequest::Any.matches(&info.version, Some(&info.path)))
282+
.expect("matching toolchain");
283+
284+
assert_eq!(selected.name, "nightly-aarch64-apple-darwin");
285+
}
286+
}

crates/prek/src/languages/rust/rust.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::languages::LanguageImpl;
1818
use crate::languages::rust::RustRequest;
1919
use crate::languages::rust::installer::RustInstaller;
2020
use crate::languages::rust::rustup::Rustup;
21-
use crate::languages::rust::version::EXTRA_KEY_CHANNEL;
21+
use crate::languages::rust::version::{Channel, EXTRA_KEY_CHANNEL};
2222
use crate::languages::version::LanguageRequest;
2323
use crate::process::Cmd;
2424
use crate::run::run_by_batch;
@@ -451,8 +451,8 @@ impl LanguageImpl for Rust {
451451
info.with_extra(EXTRA_KEY_CHANNEL, &channel.to_string());
452452
}
453453
RustRequest::Any => {
454-
// Any resolves to "stable" in resolve_version
455-
info.with_extra(EXTRA_KEY_CHANNEL, "stable");
454+
let channel = rust.version().channel().unwrap_or(Channel::Stable);
455+
info.with_extra(EXTRA_KEY_CHANNEL, &channel.to_string());
456456
}
457457
_ => {}
458458
}

crates/prek/src/languages/rust/version.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ impl RustVersion {
106106
)
107107
}
108108
}
109+
110+
pub(crate) fn channel(&self) -> Option<Channel> {
111+
self.channel
112+
}
109113
}
110114

111115
/// `language_version` field of rust can be one of the following:

0 commit comments

Comments
 (0)