diff --git a/Cargo.lock b/Cargo.lock index f5104467f2..4b89d39a96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,6 +20,7 @@ dependencies = [ "tempdir 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "term 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "user32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -517,6 +518,7 @@ dependencies = [ "scopeguard 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "shell32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "userenv-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "winreg 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index aa212bd989..30b99aafca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ scopeguard = "0.1.2" rustc-serialize = "0.3" sha2 = "0.1.2" markdown = { git="https://github.com/Diggsey/markdown.rs.git" } +toml = "0.1.27" [target."cfg(windows)".dependencies] winapi = "0.2.4" diff --git a/src/rustup-cli/common.rs b/src/rustup-cli/common.rs index 9bd92fbc1e..7698bd7273 100644 --- a/src/rustup-cli/common.rs +++ b/src/rustup-cli/common.rs @@ -9,7 +9,6 @@ use self_update; use std::io::{Write, Read, BufRead}; use std::process::Command; use std::{cmp, iter}; -use std::str::FromStr; use std; use term2; @@ -321,19 +320,15 @@ pub fn list_toolchains(cfg: &Cfg) -> Result<()> { } pub fn list_overrides(cfg: &Cfg) -> Result<()> { - let mut overrides = try!(cfg.override_db.list()); - - overrides.sort(); + let overrides = try!(cfg.settings_file.with(|s| Ok(s.overrides.clone()))); if overrides.is_empty() { println!("no overrides"); } else { - for o in overrides { - split_override::(&o, ';').map(|li| - println!("{:<40}\t{:<20}", - utils::format_path_for_display(&li.0), - li.1) - ); + for (k, v) in overrides { + println!("{:<40}\t{:<20}", + utils::format_path_for_display(&k), + v) } } Ok(()) @@ -344,15 +339,6 @@ pub fn version() -> &'static str { concat!(env!("CARGO_PKG_VERSION"), include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"))) } -fn split_override(s: &str, separator: char) -> Option<(T, T)> { - s.find(separator).and_then(|index| { - match (T::from_str(&s[..index]), T::from_str(&s[index + 1..])) { - (Ok(l), Ok(r)) => Some((l, r)), - _ => None - } - }) -} - pub fn report_error(e: &Error) { err!("{}", e); diff --git a/src/rustup-cli/multirust_mode.rs b/src/rustup-cli/multirust_mode.rs index 611dd5c9f1..d1bfceaedf 100644 --- a/src/rustup-cli/multirust_mode.rs +++ b/src/rustup-cli/multirust_mode.rs @@ -265,15 +265,13 @@ fn remove_override(cfg: &Cfg, m: &ArgMatches) -> Result<()> { let ref path = m.value_of("override") .map(|p| PathBuf::from(p)).unwrap_or(cwd); - if try!(cfg.override_db.find(path, cfg.notify_handler.as_ref())).is_none() { + if try!(cfg.settings_file.with_mut(|s| { + Ok(s.remove_override(path, cfg.notify_handler.as_ref())) + })) { + info!("override toolchain for '{}' removed", path.display()); + } else { info!("no override toolchain for '{}'", path.display()); - return Ok(()); } - - try!(cfg.override_db.remove(path, - &cfg.temp_cfg, - cfg.notify_handler.as_ref())); - info!("override toolchain for '{}' removed", path.display()); Ok(()) } diff --git a/src/rustup-cli/rustup_mode.rs b/src/rustup-cli/rustup_mode.rs index a894484c40..5bb2341e97 100644 --- a/src/rustup-cli/rustup_mode.rs +++ b/src/rustup-cli/rustup_mode.rs @@ -440,16 +440,13 @@ fn override_add(cfg: &Cfg, m: &ArgMatches) -> Result<()> { fn override_remove(cfg: &Cfg) -> Result<()> { let ref path = try!(utils::current_dir()); - let ref override_db = cfg.override_db; - let notify_handler = cfg.notify_handler.as_ref(); - - if try!(override_db.find(path, notify_handler)).is_none() { + if try!(cfg.settings_file.with_mut(|s| { + Ok(s.remove_override(path, cfg.notify_handler.as_ref())) + })) { + info!("override toolchain for '{}' removed", path.display()); + } else { info!("no override toolchain for '{}'", path.display()); - return Ok(()); } - - try!(override_db.remove(path, &cfg.temp_cfg, notify_handler)); - info!("override toolchain for '{}' removed", path.display()); Ok(()) } diff --git a/src/rustup-dist/src/config.rs b/src/rustup-dist/src/config.rs index 73e9f1e50f..639fa447a0 100644 --- a/src/rustup-dist/src/config.rs +++ b/src/rustup-dist/src/config.rs @@ -1,6 +1,6 @@ use toml; -use toml_utils::*; +use rustup_utils::toml_utils::*; use errors::*; use super::manifest::Component; diff --git a/src/rustup-dist/src/errors.rs b/src/rustup-dist/src/errors.rs index 82df5ae0be..1a06cfb843 100644 --- a/src/rustup-dist/src/errors.rs +++ b/src/rustup-dist/src/errors.rs @@ -93,10 +93,6 @@ error_chain! { Parsing(e: Vec) { description("error parsing manifest") } - ExpectedType(t: &'static str, n: String) { - description("expected type") - display("expected type: '{}' for '{}'", t, n) - } UnsupportedVersion(v: String) { description("unsupported manifest version") display("manifest version '{}' is not supported", v) @@ -116,7 +112,7 @@ fn component_unavailable_msg(cs: &[Component]) -> String { assert!(!cs.is_empty()); let mut buf = vec![]; - + if cs.len() == 1 { let _ = write!(buf, "component '{}' for '{}' is unavailable for download", cs[0].pkg, cs[0].target); diff --git a/src/rustup-dist/src/lib.rs b/src/rustup-dist/src/lib.rs index 3eaa0a9475..a667129fbe 100644 --- a/src/rustup-dist/src/lib.rs +++ b/src/rustup-dist/src/lib.rs @@ -27,4 +27,3 @@ pub mod manifestation; pub mod download; pub mod manifest; pub mod config; -mod toml_utils; diff --git a/src/rustup-dist/src/manifest.rs b/src/rustup-dist/src/manifest.rs index 5abfccda87..9873b569c2 100644 --- a/src/rustup-dist/src/manifest.rs +++ b/src/rustup-dist/src/manifest.rs @@ -12,7 +12,7 @@ use errors::*; use toml; -use toml_utils::*; +use rustup_utils::toml_utils::*; use std::collections::HashMap; use dist::TargetTriple; diff --git a/src/rustup-utils/Cargo.toml b/src/rustup-utils/Cargo.toml index c14f38fc52..b00b957a39 100644 --- a/src/rustup-utils/Cargo.toml +++ b/src/rustup-utils/Cargo.toml @@ -19,6 +19,7 @@ libc = "0.2.0" native-tls = { git = "https://github.com/sfackler/rust-native-tls.git" } rustc-serialize = "0.3.19" sha2 = "0.1.2" +toml = "0.1.27" [target.'cfg(not(any(target_os = "windows", target_os = "macos")))'.dependencies] openssl-sys = "0.7.11" diff --git a/src/rustup-utils/src/errors.rs b/src/rustup-utils/src/errors.rs index 1c54032e63..9c73fa8612 100644 --- a/src/rustup-utils/src/errors.rs +++ b/src/rustup-utils/src/errors.rs @@ -43,6 +43,10 @@ error_chain! { description("could not create directory") display("could not crate {} directory: '{}'", name, path.display()) } + ExpectedType(t: &'static str, n: String) { + description("expected type") + display("expected type: '{}' for '{}'", t, n) + } FilteringFile { name: &'static str, src: PathBuf, diff --git a/src/rustup-utils/src/lib.rs b/src/rustup-utils/src/lib.rs index b1e3a2b807..9e047e9d5c 100644 --- a/src/rustup-utils/src/lib.rs +++ b/src/rustup-utils/src/lib.rs @@ -10,6 +10,7 @@ extern crate error_chain; extern crate native_tls; extern crate rustc_serialize; extern crate sha2; +extern crate toml; #[cfg(not(any(target_os = "windows", target_os = "macos")))] extern crate openssl_sys; @@ -37,6 +38,7 @@ pub mod notifications; pub mod raw; pub mod tty; pub mod utils; +pub mod toml_utils; pub use errors::*; pub use notifications::{Notification, NotifyHandler}; diff --git a/src/rustup-dist/src/toml_utils.rs b/src/rustup-utils/src/toml_utils.rs similarity index 81% rename from src/rustup-dist/src/toml_utils.rs rename to src/rustup-utils/src/toml_utils.rs index c2f388cd4b..32a8a468d9 100644 --- a/src/rustup-dist/src/toml_utils.rs +++ b/src/rustup-utils/src/toml_utils.rs @@ -16,6 +16,18 @@ pub fn get_string(table: &mut toml::Table, key: &str, path: &str) -> Result Result> { + if let Ok(v) = get_value(table, key, path) { + if let toml::Value::String(s) = v { + Ok(Some(s)) + } else { + Err(ErrorKind::ExpectedType("string", path.to_owned() + key).into()) + } + } else { + Ok(None) + } +} + pub fn get_bool(table: &mut toml::Table, key: &str, path: &str) -> Result { get_value(table, key, path).and_then(|v| { if let toml::Value::Boolean(b) = v { diff --git a/src/rustup/config.rs b/src/rustup/config.rs index 4ed3fd55f1..df72a17a50 100644 --- a/src/rustup/config.rs +++ b/src/rustup/config.rs @@ -12,13 +12,10 @@ use errors::*; use notifications::*; use rustup_dist::{temp, dist}; use rustup_utils::utils; -use override_db::OverrideDB; use toolchain::{Toolchain, UpdateStatus}; use telemetry::{TelemetryMode}; use telemetry_analysis::*; - -// Note: multirust-rs jumped from 2 to 12 to leave multirust.sh room to diverge -pub const METADATA_VERSION: &'static str = "12"; +use settings::{SettingsFile, DEFAULT_METADATA_VERSION}; #[derive(Debug)] pub enum OverrideReason { @@ -42,9 +39,7 @@ impl Display for OverrideReason { #[derive(Debug)] pub struct Cfg { pub multirust_dir: PathBuf, - pub version_file: PathBuf, - pub override_db: OverrideDB, - pub default_file: PathBuf, + pub settings_file: SettingsFile, pub toolchains_dir: PathBuf, pub update_hash_dir: PathBuf, pub temp_cfg: temp::Cfg, @@ -62,10 +57,47 @@ impl Cfg { try!(utils::ensure_dir_exists("home", &multirust_dir, ntfy!(¬ify_handler))); + let settings_file = SettingsFile(multirust_dir.join("settings.toml")); + // Data locations - let version_file = multirust_dir.join("version"); - let override_db = OverrideDB::new(multirust_dir.join("overrides")); - let default_file = multirust_dir.join("default"); + let legacy_version_file = multirust_dir.join("version"); + if utils::is_file(&legacy_version_file) { + fn split_override(s: &str, separator: char) -> Option<(T, T)> { + s.find(separator).and_then(|index| { + match (T::from_str(&s[..index]), T::from_str(&s[index + 1..])) { + (Ok(l), Ok(r)) => Some((l, r)), + _ => None + } + }) + } + + let override_db = multirust_dir.join("overrides"); + let default_file = multirust_dir.join("default"); + // Legacy upgrade + try!(settings_file.with_mut(|s| { + s.version = try!(utils::read_file("version", &legacy_version_file)) + .trim().to_owned(); + + if utils::is_file(&default_file) { + s.default_toolchain = Some(try!(utils::read_file("default", &default_file)) + .trim().to_owned()); + } + if utils::is_file(&override_db) { + let overrides = try!(utils::read_file("overrides", &override_db)); + for o in overrides.lines() { + if let Some((k, v)) = split_override(o, ';') { + s.overrides.insert(k, v); + } + } + } + Ok(()) + })); + + // Failure to delete these is not a fatal error + let _ = utils::remove_file("version", &legacy_version_file); + let _ = utils::remove_file("default", &default_file); + let _ = utils::remove_file("overrides", &override_db); + } let toolchains_dir = multirust_dir.join("toolchains"); let update_hash_dir = multirust_dir.join("update-hashes"); @@ -97,9 +129,7 @@ impl Cfg { Ok(Cfg { multirust_dir: multirust_dir, - version_file: version_file, - override_db: override_db, - default_file: default_file, + settings_file: settings_file, toolchains_dir: toolchains_dir, update_hash_dir: update_hash_dir, temp_cfg: temp_cfg, @@ -112,14 +142,11 @@ impl Cfg { } pub fn set_default(&self, toolchain: &str) -> Result<()> { - let work_file = try!(self.temp_cfg.new_file()); - - try!(utils::write_file("temp", &work_file, toolchain)); - - try!(utils::rename_file("default", &*work_file, &self.default_file)); - + try!(self.settings_file.with_mut(|s| { + s.default_toolchain = Some(toolchain.to_owned()); + Ok(()) + })); self.notify_handler.call(Notification::SetDefaultToolchain(toolchain)); - Ok(()) } @@ -159,28 +186,19 @@ impl Cfg { } pub fn upgrade_data(&self) -> Result<()> { - if !utils::is_file(&self.version_file) { - return Ok(()); - } - let mut current_version = try!(utils::read_file("version", &self.version_file)); - let len = current_version.trim_right().len(); - current_version.truncate(len); + let current_version = try!(self.settings_file.with(|s| Ok(s.version.clone()))); - if current_version == METADATA_VERSION { + if current_version == DEFAULT_METADATA_VERSION { self.notify_handler - .call(Notification::MetadataUpgradeNotNeeded(METADATA_VERSION)); + .call(Notification::MetadataUpgradeNotNeeded(¤t_version)); return Ok(()); } self.notify_handler - .call(Notification::UpgradingMetadata(¤t_version, METADATA_VERSION)); + .call(Notification::UpgradingMetadata(¤t_version, DEFAULT_METADATA_VERSION)); match &*current_version { - "1" => { - // This corresponds to an old version of multirust.sh. - Err(ErrorKind::UnknownMetadataVersion(current_version).into()) - } "2" => { // The toolchain installation format changed. Just delete them all. self.notify_handler @@ -200,9 +218,10 @@ impl Cfg { try!(utils::remove_file("update hash", &file.path())); } - try!(utils::write_file("version", &self.version_file, METADATA_VERSION)); - - Ok(()) + self.settings_file.with_mut(|s| { + s.version = DEFAULT_METADATA_VERSION.to_owned(); + Ok(()) + }) } _ => Err(ErrorKind::UnknownMetadataVersion(current_version).into()), } @@ -217,19 +236,16 @@ impl Cfg { } pub fn find_default(&self) -> Result> { - if !utils::is_file(&self.default_file) { - return Ok(None); - } - let content = try!(utils::read_file("default", &self.default_file)); - let name = content.trim_matches('\n'); - if name.is_empty() { - return Ok(None); - } + let opt_name = try!(self.settings_file.with(|s| Ok(s.default_toolchain.clone()))); - let toolchain = try!(self.verify_toolchain(name) - .chain_err(|| ErrorKind::ToolchainNotInstalled(name.to_string()))); + if let Some(name) = opt_name { + let toolchain = try!(self.verify_toolchain(&name) + .chain_err(|| ErrorKind::ToolchainNotInstalled(name.to_string()))); - Ok(Some(toolchain)) + Ok(Some(toolchain)) + } else { + Ok(None) + } } pub fn find_override(&self, path: &Path) -> Result> { @@ -239,8 +255,10 @@ impl Cfg { return Ok(Some((toolchain, OverrideReason::Environment))); } - if let Some((name, reason_path)) = try!(self.override_db - .find(path, self.notify_handler.as_ref())) { + let result = try!(self.settings_file.with(|s| { + Ok(s.find_override(path, self.notify_handler.as_ref())) + })); + if let Some((name, reason_path)) = result { let toolchain = try!(self.verify_toolchain(&name).chain_err(|| ErrorKind::ToolchainNotInstalled(name.to_string()))); return Ok(Some((toolchain, OverrideReason::OverrideDB(reason_path)))); } @@ -293,23 +311,14 @@ impl Cfg { pub fn check_metadata_version(&self) -> Result<()> { try!(utils::assert_is_directory(&self.multirust_dir)); - if !utils::is_file(&self.version_file) { - self.notify_handler.call(Notification::WritingMetadataVersion(METADATA_VERSION)); - - try!(utils::write_file("metadata version", &self.version_file, METADATA_VERSION)); - - Ok(()) - } else { - let current_version = try!(utils::read_file("metadata version", &self.version_file)); - - self.notify_handler.call(Notification::ReadMetadataVersion(¤t_version)); - - if &*current_version == METADATA_VERSION { + self.settings_file.with(|s| { + self.notify_handler.call(Notification::ReadMetadataVersion(&s.version)); + if s.version == DEFAULT_METADATA_VERSION { Ok(()) } else { Err(ErrorKind::NeedMetadataUpgrade.into()) } - } + }) } pub fn toolchain_for_dir(&self, path: &Path) -> Result<(Toolchain, Option)> { @@ -394,7 +403,7 @@ impl Cfg { fn enable_telemetry(&self) -> Result<()> { let work_file = try!(self.temp_cfg.new_file()); - + let _ = utils::ensure_dir_exists("telemetry", &self.multirust_dir.join("telemetry"), ntfy!(&NotifyHandler::none())); try!(utils::write_file("temp", &work_file, "")); diff --git a/src/rustup/errors.rs b/src/rustup/errors.rs index 5a4445524a..9c526c0148 100644 --- a/src/rustup/errors.rs +++ b/src/rustup/errors.rs @@ -1,6 +1,7 @@ use rustup_dist::{self, temp}; use rustup_utils; use rustup_dist::manifest::Component; +use toml; error_chain! { types { @@ -49,6 +50,9 @@ error_chain! { display("component '{}' for target '{}' is required for toolchain '{}' and cannot be re-added", c.pkg, c.target, t) } + ParsingSettings(e: Vec) { + description("error parsing settings") + } RemovingRequiredComponent(t: String, c: Component) { description("required component cannot be removed") display("component '{}' for target '{}' is required for toolchain '{}' and cannot be removed", diff --git a/src/rustup/lib.rs b/src/rustup/lib.rs index 2992225e63..45b5dcc171 100644 --- a/src/rustup/lib.rs +++ b/src/rustup/lib.rs @@ -10,6 +10,7 @@ extern crate regex; extern crate itertools; extern crate rustc_serialize; extern crate time; +extern crate toml; #[cfg(unix)] extern crate libc; @@ -17,16 +18,15 @@ pub use errors::*; pub use notifications::*; pub use config::*; pub use toolchain::*; -pub use override_db::*; -pub use rustup_utils::{utils, notify}; +pub use rustup_utils::{utils, notify, toml_utils}; mod errors; mod notifications; -mod override_db; mod toolchain; mod config; mod env_var; mod install; +mod settings; pub mod telemetry; pub mod command; pub mod telemetry_analysis; diff --git a/src/rustup/override_db.rs b/src/rustup/override_db.rs deleted file mode 100644 index 3e2079ad37..0000000000 --- a/src/rustup/override_db.rs +++ /dev/null @@ -1,117 +0,0 @@ -use std::path::{Path, PathBuf}; - -use errors::*; -use notifications::*; -use rustup_dist::temp; -use rustup_utils::utils; - -pub const DB_DELIMITER: &'static str = ";"; - -#[derive(Debug)] -pub struct OverrideDB(PathBuf); - -impl OverrideDB { - fn path_to_db_key(&self, path: &Path, notify_handler: NotifyHandler) -> Result { - Ok(utils::canonicalize_path(path, ntfy!(¬ify_handler)) - .display() - .to_string() + DB_DELIMITER) - } - - pub fn new(path: PathBuf) -> Self { - OverrideDB(path) - } - - pub fn remove(&self, - path: &Path, - temp_cfg: &temp::Cfg, - notify_handler: NotifyHandler) - -> Result { - let key = try!(self.path_to_db_key(path, notify_handler)); - - let work_file = try!(temp_cfg.new_file()); - - let removed = if utils::is_file(&self.0) { - try!(utils::filter_file("override db", - &self.0, - &work_file, - |line| !line.starts_with(&key))) - } else { - 0 - }; - - if removed > 0 { - try!(utils::rename_file("override db", &*work_file, &self.0)); - Ok(true) - } else { - Ok(false) - } - } - - pub fn set(&self, - path: &Path, - toolchain: &str, - temp_cfg: &temp::Cfg, - notify_handler: NotifyHandler) - -> Result<()> { - let key = try!(self.path_to_db_key(path, notify_handler)); - - let work_file = try!(temp_cfg.new_file()); - - if utils::is_file(&self.0) { - try!(utils::filter_file("override db", - &self.0, - &work_file, - |line| !line.starts_with(&key))); - } - - try!(utils::append_file("override db", &work_file, &(key + toolchain))); - - try!(utils::rename_file("override db", &*work_file, &self.0)); - - notify_handler.call(Notification::SetOverrideToolchain(path, toolchain)); - - Ok(()) - } - - pub fn find(&self, - dir_unresolved: &Path, - notify_handler: NotifyHandler) - -> Result> { - if !utils::is_file(&self.0) { - return Ok(None); - } - - let dir = utils::canonicalize_path(dir_unresolved, ntfy!(¬ify_handler)); - let mut maybe_path = Some(&*dir); - while let Some(path) = maybe_path { - let key = try!(self.path_to_db_key(path, notify_handler)); - if let Some(toolchain) = try!(utils::match_file("override db", &self.0, |line| { - if line.starts_with(&key) { - Some(line[key.len()..].to_owned()) - } else { - None - } - })) { - return Ok(Some((toolchain, path.to_owned()))); - } - - maybe_path = path.parent(); - } - - Ok(None) - } - - pub fn list(&self) -> Result> { - if utils::is_file(&self.0) { - let contents = try!(utils::read_file("override db", &self.0)); - - let overrides: Vec<_> = contents.lines() - .map(|s| s.to_owned()) - .collect(); - - Ok(overrides) - } else { - Ok(Vec::new()) - } - } -} diff --git a/src/rustup/settings.rs b/src/rustup/settings.rs new file mode 100644 index 0000000000..803f1a358f --- /dev/null +++ b/src/rustup/settings.rs @@ -0,0 +1,146 @@ +use errors::*; +use notifications::*; +use toml_utils::*; +use utils; +use toml; +use std::collections::BTreeMap; +use std::path::{Path, PathBuf}; + +pub const SUPPORTED_METADATA_VERSIONS: [&'static str; 2] = ["2", "12"]; +pub const DEFAULT_METADATA_VERSION: &'static str = "12"; + + +#[derive(Clone, Debug, PartialEq)] +pub struct SettingsFile(pub PathBuf); + +impl SettingsFile { + fn write_settings(&self, settings: Settings) -> Result<()> { + try!(utils::write_file("settings", &self.0, &settings.stringify())); + Ok(()) + } + fn read_settings(&self) -> Result { + if !utils::is_file(&self.0) { + try!(self.write_settings(Default::default())); + } + let content = try!(utils::read_file("settings", &self.0)); + Settings::parse(&content) + } + pub fn with Result>(&self, f: F) -> Result { + f(&try!(self.read_settings())) + } + pub fn with_mut Result>(&self, f: F) -> Result { + let mut settings = try!(self.read_settings()); + let result = try!(f(&mut settings)); + try!(self.write_settings(settings)); + Ok(result) + } +} + + +#[derive(Clone, Debug, PartialEq)] +pub struct Settings { + pub version: String, + pub default_toolchain: Option, + pub overrides: BTreeMap, +} + +impl Default for Settings { + fn default() -> Self { + Settings { + version: DEFAULT_METADATA_VERSION.to_owned(), + default_toolchain: None, + overrides: BTreeMap::new() + } + } +} + +impl Settings { + fn path_to_key(path: &Path, notify_handler: NotifyHandler) -> String { + utils::canonicalize_path(path, ntfy!(¬ify_handler)) + .display() + .to_string() + } + + pub fn remove_override(&mut self, path: &Path, notify_handler: NotifyHandler) -> bool { + let key = Self::path_to_key(path, notify_handler); + self.overrides.remove(&key).is_some() + } + + pub fn add_override(&mut self, path: &Path, toolchain: String, notify_handler: NotifyHandler) { + let key = Self::path_to_key(path, notify_handler); + notify_handler.call(Notification::SetOverrideToolchain(path, &toolchain)); + self.overrides.insert(key, toolchain); + } + + pub fn find_override(&self, dir_unresolved: &Path, notify_handler: NotifyHandler) + -> Option<(String, PathBuf)> { + let dir = utils::canonicalize_path(dir_unresolved, ntfy!(¬ify_handler)); + let mut maybe_path = Some(&*dir); + while let Some(path) = maybe_path { + let key = Self::path_to_key(path, notify_handler); + if let Some(toolchain) = self.overrides.get(&key) { + return Some((toolchain.to_owned(), path.to_owned())); + } + maybe_path = path.parent(); + } + None + } + + pub fn parse(data: &str) -> Result { + let mut parser = toml::Parser::new(data); + let value = try!(parser.parse().ok_or_else(move || ErrorKind::ParsingSettings(parser.errors))); + + Self::from_toml(value, "") + } + pub fn stringify(self) -> String { + toml::Value::Table(self.to_toml()).to_string() + } + + pub fn from_toml(mut table: toml::Table, path: &str) -> Result { + let version = try!(get_string(&mut table, "version", path)); + if !SUPPORTED_METADATA_VERSIONS.contains(&&*version) { + return Err(ErrorKind::UnknownMetadataVersion(version).into()); + } + Ok(Settings { + version: version, + default_toolchain: try!(get_opt_string(&mut table, "default_toolchain", path)), + overrides: try!(Self::table_to_overrides(table, path)), + }) + } + pub fn to_toml(self) -> toml::Table { + let mut result = toml::Table::new(); + + result.insert("version".to_owned(), + toml::Value::String(self.version)); + + if let Some(v) = self.default_toolchain { + result.insert("default_toolchain".to_owned(), toml::Value::String(v)); + } + + let overrides = Self::overrides_to_table(self.overrides); + result.insert("overrides".to_owned(), toml::Value::Table(overrides)); + + result + } + + fn table_to_overrides(mut table: toml::Table, path: &str) -> Result> { + let mut result = BTreeMap::new(); + let pkg_table = try!(get_table(&mut table, "overrides", path)); + + for (k, v) in pkg_table { + if let toml::Value::String(t) = v { + result.insert(k, t); + } + } + + Ok(result) + } + + fn overrides_to_table(overrides: BTreeMap) -> toml::Table { + let mut result = toml::Table::new(); + for (k, v) in overrides { + result.insert(k, toml::Value::String(v)); + } + result + } +} diff --git a/src/rustup/toolchain.rs b/src/rustup/toolchain.rs index 113f33cc78..e580d6e3d6 100644 --- a/src/rustup/toolchain.rs +++ b/src/rustup/toolchain.rs @@ -158,7 +158,7 @@ impl<'a> Toolchain<'a> { self.install(InstallMethod::Dist(&try!(self.desc()), update_hash.as_ref().map(|p| &**p), self.download_cfg())) - } + } pub fn install_from_dist_with_telemetry(&self) -> Result { let result = self.install_from_dist_inner(); @@ -169,12 +169,12 @@ impl<'a> Toolchain<'a> { success: true }; match self.telemetry.log_telemetry(te) { Ok(_) => Ok(us), - Err(e) => { + Err(e) => { self.cfg.notify_handler.call(Notification::TelemetryCleanupError(&e)); Ok(us) } } - } + } Err(e) => { let te = TelemetryEvent::ToolchainUpdate { toolchain: self.name().to_string() , success: true }; @@ -333,10 +333,10 @@ impl<'a> Toolchain<'a> { self.cfg.set_default(&self.name) } pub fn make_override(&self, path: &Path) -> Result<()> { - Ok(try!(self.cfg.override_db.set(path, - &self.name, - &self.cfg.temp_cfg, - self.cfg.notify_handler.as_ref()))) + Ok(try!(self.cfg.settings_file.with_mut(|s| { + s.add_override(path, self.name.clone(), self.cfg.notify_handler.as_ref()); + Ok(()) + }))) } pub fn list_components(&self) -> Result> { @@ -404,19 +404,19 @@ impl<'a> Toolchain<'a> { match output { Ok(_) => { - let te = TelemetryEvent::ToolchainUpdate { toolchain: self.name.to_owned(), + let te = TelemetryEvent::ToolchainUpdate { toolchain: self.name.to_owned(), success: true }; match self.telemetry.log_telemetry(te) { Ok(_) => Ok(()), - Err(e) => { + Err(e) => { self.cfg.notify_handler.call(Notification::TelemetryCleanupError(&e)); Ok(()) } } }, Err(e) => { - let te = TelemetryEvent::ToolchainUpdate { toolchain: self.name.to_owned(), + let te = TelemetryEvent::ToolchainUpdate { toolchain: self.name.to_owned(), success: false }; let _ = self.telemetry.log_telemetry(te).map_err(|xe| {