Skip to content

Commit 2c87185

Browse files
committed
Make the toolchain overriding config hierarchical
1 parent 0cc274e commit 2c87185

File tree

1 file changed

+64
-15
lines changed

1 file changed

+64
-15
lines changed

src/config.rs

+64-15
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,15 @@ impl OverrideFile {
5555
fn is_empty(&self) -> bool {
5656
self.toolchain.is_empty()
5757
}
58+
59+
fn has_toolchain(&self) -> bool {
60+
self.toolchain.has_toolchain()
61+
}
62+
63+
/// A left-biased merge of two [`OverrideFile`]s.
64+
fn merge(&mut self, other: Self) {
65+
self.toolchain.merge(other.toolchain)
66+
}
5867
}
5968

6069
#[derive(Debug, Default, Deserialize, PartialEq, Eq)]
@@ -69,9 +78,33 @@ struct ToolchainSection {
6978
impl ToolchainSection {
7079
fn is_empty(&self) -> bool {
7180
self.channel.is_none()
81+
&& self.path.is_none()
7282
&& self.components.is_none()
7383
&& self.targets.is_none()
74-
&& self.path.is_none()
84+
}
85+
86+
fn has_toolchain(&self) -> bool {
87+
self.channel.is_some() || self.path.is_some()
88+
}
89+
90+
/// A left-biased merge of two [`ToolchainSelection`]s.
91+
///
92+
/// If a field appears in both operands, the one from the left-hand side is selected.
93+
fn merge(&mut self, other: Self) {
94+
if !self.has_toolchain() {
95+
// `channel` and `path` are mutually exclusive, so they need to be updated together,
96+
// and this will happen only if `self` doesn't have a toolchain yet.
97+
(self.channel, self.path) = (other.channel, other.path);
98+
}
99+
if self.components.is_none() {
100+
self.components = other.components;
101+
}
102+
if self.targets.is_none() {
103+
self.targets = other.targets;
104+
}
105+
if self.profile.is_none() {
106+
self.profile = other.profile;
107+
}
75108
}
76109
}
77110

@@ -96,6 +129,7 @@ impl<T: Into<String>> From<T> for OverrideFile {
96129
}
97130
}
98131

132+
/// The reason for which the toolchain is overridden.
99133
#[derive(Debug)]
100134
pub(crate) enum OverrideReason {
101135
Environment,
@@ -453,31 +487,46 @@ impl Cfg {
453487
}
454488

455489
fn find_override_config(&self, path: &Path) -> Result<Option<(OverrideCfg, OverrideReason)>> {
456-
let mut override_ = None;
490+
let mut override_ = None::<(OverrideFile, OverrideReason)>;
491+
492+
let mut update_override = |file, reason| {
493+
if let Some((file1, reason1)) = &mut override_ {
494+
if file1.has_toolchain() {
495+
// Update the reason only if the override file has a toolchain.
496+
*reason1 = reason
497+
}
498+
file1.merge(file);
499+
} else {
500+
override_ = Some((file, reason))
501+
};
502+
};
503+
504+
// Check for all possible toolchain overrides below...
505+
// See: <https://rust-lang.github.io/rustup/overrides.html>
457506

458-
// First check toolchain override from command
507+
// 1. Check toolchain override from command
459508
if let Some(ref name) = self.toolchain_override {
460-
override_ = Some((name.to_string().into(), OverrideReason::CommandLine));
509+
update_override(name.to_string().into(), OverrideReason::CommandLine);
461510
}
462511

463-
// Check RUSTUP_TOOLCHAIN
512+
// 2. Check RUSTUP_TOOLCHAIN
464513
if let Some(ref name) = self.env_override {
465514
// Because path based toolchain files exist, this has to support
466515
// custom, distributable, and absolute path toolchains otherwise
467516
// rustup's export of a RUSTUP_TOOLCHAIN when running a process will
468517
// error when a nested rustup invocation occurs
469-
override_ = Some((name.to_string().into(), OverrideReason::Environment));
518+
update_override(name.to_string().into(), OverrideReason::Environment);
470519
}
471520

472-
// Then walk up the directory tree from 'path' looking for either the
473-
// directory in override database, or a `rust-toolchain` file.
474-
if override_.is_none() {
475-
self.settings_file.with(|s| {
476-
override_ = self.find_override_from_dir_walk(path, s)?;
477-
478-
Ok(())
479-
})?;
480-
}
521+
// 3. walk up the directory tree from 'path' looking for either the
522+
// directory in override database, or
523+
// 4. a `rust-toolchain` file.
524+
self.settings_file.with(|s| {
525+
if let Some((file, reason)) = self.find_override_from_dir_walk(path, s)? {
526+
update_override(file, reason);
527+
}
528+
Ok(())
529+
})?;
481530

482531
if let Some((file, reason)) = override_ {
483532
// This is hackishly using the error chain to provide a bit of

0 commit comments

Comments
 (0)