Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions src/cli/rustup_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{
env::consts::EXE_SUFFIX,
fmt,
io::{self, Write},
path::{Path, PathBuf},
path::{self, Path, PathBuf},
process::ExitStatus,
str::FromStr,
sync::Arc,
Expand Down Expand Up @@ -1750,7 +1750,13 @@ async fn override_add(
Err(e) => Err(e)?,
}

cfg.make_override(path.unwrap_or(&cfg.current_dir), &toolchain)?;
// Relative to the directory rustup runs in, which `path::absolute` alone
// would take from the OS rather than from `cfg`.
let path = match path {
Some(path) => &path::absolute(cfg.current_dir.join(path))?,
None => &cfg.current_dir,
};
cfg.make_override(path, &toolchain)?;
Ok(ExitCode::SUCCESS)
}

Expand All @@ -1774,7 +1780,7 @@ fn override_remove(
}
list
} else if let Some(path) = path {
vec![path.to_owned()]
vec![path::absolute(cfg.current_dir.join(path))?]
} else {
vec![cfg.current_dir.clone()]
};
Expand Down
5 changes: 3 additions & 2 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -687,14 +687,15 @@ impl<'a> Cfg<'a> {
dir: &Path,
settings: &Settings,
) -> anyhow::Result<Option<(OverrideCfg, ActiveSource)>> {
let overrides = settings.overrides_by_dir();
let mut dir = Some(dir);

while let Some(d) = dir {
// First check the override database
if let Some(name) = settings.dir_override(d) {
if let Some(name) = overrides.get(d) {
let source = ActiveSource::OverrideDb(d.to_owned());
return Ok(Some((
Override::<PartialToolchainName>::from_str(&name)?
Override::<PartialToolchainName>::from_str(name)?
.resolve(self)?
.into(),
source,
Expand Down
62 changes: 47 additions & 15 deletions src/settings.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use std::{
cell::RefCell,
collections::BTreeMap,
collections::{BTreeMap, HashMap},
fmt,
path::{Path, PathBuf},
str::FromStr,
};

use anyhow::Context;
use same_file::Handle;
use serde::{Deserialize, Serialize};
use tracing::info;

Expand Down Expand Up @@ -104,31 +105,41 @@ pub struct Settings {
}

impl Settings {
fn path_to_key(path: &Path) -> String {
if path.exists() {
utils::canonicalize_path(path).display().to_string()
} else {
path.display().to_string()
}
}

pub(crate) fn remove_override(&mut self, path: &Path) -> bool {
let key = Self::path_to_key(path);
self.overrides.remove(&key).is_some()
let mut removed = self.overrides.remove(&path.display().to_string()).is_some();
// The directory may also be stored under other paths that lead to it.
if let Ok(dir) = Handle::from_path(path) {
let count = self.overrides.len();
self.overrides.retain(|stored, _| !leads_to(stored, &dir));
removed |= self.overrides.len() < count;
}
removed
}

pub(crate) fn add_override(&mut self, path: &Path, toolchain: String) {
let key = Self::path_to_key(path);
let key = path.display().to_string();
// Drop every entry stored for this directory under any path, so that
// the one inserted below under `key` is its only override.
if let Ok(dir) = Handle::from_path(path) {
self.overrides.retain(|stored, _| !leads_to(stored, &dir));
}
info!(
"override toolchain for {} set to {toolchain}",
path.display(),
);
self.overrides.insert(key, toolchain);
}

pub(crate) fn dir_override(&self, dir: &Path) -> Option<String> {
let key = Self::path_to_key(dir);
self.overrides.get(&key).cloned()
/// Indexes the overrides by the directories they refer to, for a series of lookups.
pub(crate) fn overrides_by_dir(&self) -> OverridesByDir<'_> {
let mut by_dir = HashMap::new();
for (path, toolchain) in &self.overrides {
// A directory that cannot be opened cannot be the current one either.
if let Ok(dir) = Handle::from_path(path) {
by_dir.entry(dir).or_insert_with(|| toolchain.as_str());
}
}
OverridesByDir { by_dir }
}

pub(crate) fn parse(data: &str) -> anyhow::Result<Self> {
Expand All @@ -140,6 +151,27 @@ impl Settings {
}
}

/// Whether `path` leads to the directory behind `dir`.
fn leads_to(path: &str, dir: &Handle) -> bool {
Handle::from_path(path).is_ok_and(|other| other == *dir)
}

/// Overrides keyed by the identity of their directories rather than by their
/// paths, so that an override applies however its directory is reached.
pub(crate) struct OverridesByDir<'a> {
by_dir: HashMap<Handle, &'a str>,
}

impl OverridesByDir<'_> {
pub(crate) fn get(&self, dir: &Path) -> Option<&str> {
if self.by_dir.is_empty() {
return None;
}
let dir = Handle::from_path(dir).ok()?;
self.by_dir.get(&dir).copied()
}
}

#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub enum MetadataVersion {
#[serde(rename = "2")]
Expand Down
Loading
Loading