diff --git a/rust-install/src/dist.rs b/rust-install/src/dist.rs index 50240265cf..00fc3b22e0 100644 --- a/rust-install/src/dist.rs +++ b/rust-install/src/dist.rs @@ -15,236 +15,248 @@ pub const DEFAULT_DIST_ROOT: &'static str = "https://static.rust-lang.org/dist"; pub const UPDATE_HASH_LEN: usize = 20; pub struct ToolchainDesc { - pub arch: Option, - pub os: Option, - pub env: Option, - pub channel: String, - pub date: Option, + pub arch: Option, + pub os: Option, + pub env: Option, + pub channel: String, + pub date: Option, } impl ToolchainDesc { - pub fn from_str(name: &str) -> Option { - let archs = ["i686", "x86_64"]; - let oses = ["pc-windows", "unknown-linux", "apple-darwin"]; - let envs = ["gnu", "msvc"]; - let channels = ["nightly", "beta", "stable"]; - - let pattern = format!( - r"^(?:({})-)?(?:({})-)?(?:({})-)?({})(?:-(\d{{4}}-\d{{2}}-\d{{2}}))?$", - archs.join("|"), oses.join("|"), envs.join("|"), channels.join("|") - ); - - let re = Regex::new(&pattern).unwrap(); - re.captures(name).map(|c| { - fn fn_map(s: &str) -> Option { - if s == "" { - None - } else { - Some(s.to_owned()) - } - } - - ToolchainDesc { - arch: c.at(1).and_then(fn_map), - os: c.at(2).and_then(fn_map), - env: c.at(3).and_then(fn_map), - channel: c.at(4).unwrap().to_owned(), - date: c.at(5).and_then(fn_map), - } - }) - } - - pub fn manifest_url(&self, dist_root: &str) -> String { - match self.date { - None => - format!("{}/channel-rust-{}", dist_root, self.channel), - Some(ref date) => - format!("{}/{}/channel-rust-{}", dist_root, date, self.channel), - } - } - - pub fn package_dir(&self, dist_root: &str) -> String { - match self.date { - None => - format!("{}", dist_root), - Some(ref date) => - format!("{}/{}", dist_root, date), - } - } - - pub fn target_triple(&self) -> Option { - let (host_arch, host_os, host_env) = get_host_triple(); - let arch = self.arch.as_ref().map(|s| &**s).unwrap_or(host_arch); - let os = self.os.as_ref().map(|s| &**s).or(host_os); - let env = self.env.as_ref().map(|s| &**s).or(host_env); - - os.map(|os| { - if let Some(ref env) = env { - format!("{}-{}-{}", arch, os, env) - } else { - format!("{}-{}", arch, os) - } - }) - } - - pub fn download_manifest<'a>(&self, cfg: DownloadCfg<'a>) -> Result> { - let url = self.manifest_url(cfg.dist_root); - let package_dir = self.package_dir(cfg.dist_root); - - let manifest = try!(download_and_check(&url, None, "", cfg)).unwrap().0; - - Ok(Manifest(manifest, package_dir)) - } - - pub fn full_spec(&self) -> String { - let triple = self.target_triple().unwrap_or_else(|| "".to_owned()); - if let Some(ref date) = self.date { - format!("{}-{}-{}", triple, &self.channel, date) - } else { - format!("{}-{} (tracking)", triple, &self.channel) - } - } - - pub fn is_tracking(&self) -> bool { - self.date.is_none() - } + pub fn from_str(name: &str) -> Option { + let archs = ["i686", "x86_64"]; + let oses = ["pc-windows", "unknown-linux", "apple-darwin"]; + let envs = ["gnu", "msvc"]; + let channels = ["nightly", "beta", "stable"]; + + let pattern = format!( + r"^(?:({})-)?(?:({})-)?(?:({})-)?({})(?:-(\d{{4}}-\d{{2}}-\d{{2}}))?$", + archs.join("|"), oses.join("|"), envs.join("|"), channels.join("|") + ); + + let re = Regex::new(&pattern).unwrap(); + re.captures(name).map(|c| { + fn fn_map(s: &str) -> Option { + if s == "" { + None + } else { + Some(s.to_owned()) + } + } + + ToolchainDesc { + arch: c.at(1).and_then(fn_map), + os: c.at(2).and_then(fn_map), + env: c.at(3).and_then(fn_map), + channel: c.at(4).unwrap().to_owned(), + date: c.at(5).and_then(fn_map), + } + }) + } + + pub fn manifest_url(&self, dist_root: &str) -> String { + match self.date { + None => format!("{}/channel-rust-{}", dist_root, self.channel), + Some(ref date) => format!("{}/{}/channel-rust-{}", dist_root, date, self.channel), + } + } + + pub fn package_dir(&self, dist_root: &str) -> String { + match self.date { + None => format!("{}", dist_root), + Some(ref date) => format!("{}/{}", dist_root, date), + } + } + + pub fn target_triple(&self) -> Option { + let (host_arch, host_os, host_env) = get_host_triple(); + let arch = self.arch.as_ref().map(|s| &**s).unwrap_or(host_arch); + let os = self.os.as_ref().map(|s| &**s).or(host_os); + let env = self.env.as_ref().map(|s| &**s).or(host_env); + + os.map(|os| { + if let Some(ref env) = env { + format!("{}-{}-{}", arch, os, env) + } else { + format!("{}-{}", arch, os) + } + }) + } + + pub fn download_manifest<'a>(&self, cfg: DownloadCfg<'a>) -> Result> { + let url = self.manifest_url(cfg.dist_root); + let package_dir = self.package_dir(cfg.dist_root); + + let manifest = try!(download_and_check(&url, None, "", cfg)).unwrap().0; + + Ok(Manifest(manifest, package_dir)) + } + + pub fn full_spec(&self) -> String { + let triple = self.target_triple().unwrap_or_else(|| "".to_owned()); + if let Some(ref date) = self.date { + format!("{}-{}-{}", triple, &self.channel, date) + } else { + format!("{}-{} (tracking)", triple, &self.channel) + } + } + + pub fn is_tracking(&self) -> bool { + self.date.is_none() + } } pub struct Manifest<'a>(temp::File<'a>, String); impl<'a> Manifest<'a> { - pub fn package_url(&self, package: &str, target_triple: &str, ext: &str) -> Result> { - let suffix = target_triple.to_owned() + ext; - Ok(try!(utils::match_file("manifest", &self.0, |line| { - if line.starts_with(package) && line.ends_with(&suffix) { - Some(format!("{}/{}", &self.1, line)) - } else { - None - } - }))) - } + pub fn package_url(&self, + package: &str, + target_triple: &str, + ext: &str) + -> Result> { + let suffix = target_triple.to_owned() + ext; + Ok(try!(utils::match_file("manifest", &self.0, |line| { + if line.starts_with(package) && line.ends_with(&suffix) { + Some(format!("{}/{}", &self.1, line)) + } else { + None + } + }))) + } } impl fmt::Display for ToolchainDesc { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if let Some(ref arch) = self.arch { - try!(write!(f, "{}-", arch)); - } - if let Some(ref os) = self.os { - try!(write!(f, "{}-", os)); - } - if let Some(ref env) = self.env { - try!(write!(f, "{}-", env)); - } - - try!(write!(f, "{}", &self.channel)); - - if let Some(ref date) = self.date { - try!(write!(f, "-{}", date)); - } - - Ok(()) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if let Some(ref arch) = self.arch { + try!(write!(f, "{}-", arch)); + } + if let Some(ref os) = self.os { + try!(write!(f, "{}-", os)); + } + if let Some(ref env) = self.env { + try!(write!(f, "{}-", env)); + } + + try!(write!(f, "{}", &self.channel)); + + if let Some(ref date) = self.date { + try!(write!(f, "-{}", date)); + } + + Ok(()) + } } -pub fn download_and_check<'a>(url_str: &str, update_hash: Option<&Path>, ext: &str, cfg: DownloadCfg<'a>) -> Result, String)>> { - let hash = try!(download_hash(url_str, cfg)); - let partial_hash: String = hash.chars().take(UPDATE_HASH_LEN).collect(); - - if let Some(hash_file) = update_hash { - if utils::is_file(hash_file) { - if let Ok(contents) = utils::read_file("update hash", hash_file) { - if contents == partial_hash { - // Skip download, update hash matches - cfg.notify_handler.call(Notification::UpdateHashMatches(&partial_hash)); - return Ok(None); - } - } else { - cfg.notify_handler.call(Notification::CantReadUpdateHash(hash_file)); - } - } else { - cfg.notify_handler.call(Notification::NoUpdateHash(hash_file)); - } - } - - let url = try!(utils::parse_url(url_str)); - let file = try!(cfg.temp_cfg.new_file_with_ext("", ext)); - - let mut hasher = Hasher::new(Type::SHA256); - try!(utils::download_file(url, &file, Some(&mut hasher), ntfy!(&cfg.notify_handler))); - let actual_hash = hasher.finish().iter() - .map(|b| format!("{:02x}", b)) - .join(""); - - if hash != actual_hash { - // Incorrect hash - return Err(Error::ChecksumFailed { url: url_str.to_owned(), expected: hash, calculated: actual_hash }); - } else { - cfg.notify_handler.call(Notification::ChecksumValid(url_str)); - } - - // TODO: Check the signature of the file - - Ok(Some((file, partial_hash))) +pub fn download_and_check<'a>(url_str: &str, + update_hash: Option<&Path>, + ext: &str, + cfg: DownloadCfg<'a>) + -> Result, String)>> { + let hash = try!(download_hash(url_str, cfg)); + let partial_hash: String = hash.chars().take(UPDATE_HASH_LEN).collect(); + + if let Some(hash_file) = update_hash { + if utils::is_file(hash_file) { + if let Ok(contents) = utils::read_file("update hash", hash_file) { + if contents == partial_hash { + // Skip download, update hash matches + cfg.notify_handler.call(Notification::UpdateHashMatches(&partial_hash)); + return Ok(None); + } + } else { + cfg.notify_handler.call(Notification::CantReadUpdateHash(hash_file)); + } + } else { + cfg.notify_handler.call(Notification::NoUpdateHash(hash_file)); + } + } + + let url = try!(utils::parse_url(url_str)); + let file = try!(cfg.temp_cfg.new_file_with_ext("", ext)); + + let mut hasher = Hasher::new(Type::SHA256); + try!(utils::download_file(url, &file, Some(&mut hasher), ntfy!(&cfg.notify_handler))); + let actual_hash = hasher.finish() + .iter() + .map(|b| format!("{:02x}", b)) + .join(""); + + if hash != actual_hash { + // Incorrect hash + return Err(Error::ChecksumFailed { + url: url_str.to_owned(), + expected: hash, + calculated: actual_hash, + }); + } else { + cfg.notify_handler.call(Notification::ChecksumValid(url_str)); + } + + // TODO: Check the signature of the file + + Ok(Some((file, partial_hash))) } #[derive(Copy, Clone)] pub struct DownloadCfg<'a> { - pub dist_root: &'a str, - pub temp_cfg: &'a temp::Cfg, - pub notify_handler: NotifyHandler<'a>, + pub dist_root: &'a str, + pub temp_cfg: &'a temp::Cfg, + pub notify_handler: NotifyHandler<'a>, } -pub fn download_dist<'a>(toolchain: &str, update_hash: Option<&Path>, cfg: DownloadCfg<'a>) -> Result, String)>> { - let desc = try!(ToolchainDesc::from_str(toolchain) - .ok_or(Error::InvalidToolchainName)); - - let target_triple = try!(desc.target_triple().ok_or_else(|| Error::UnsupportedHost(desc.full_spec()))); - let ext = get_installer_ext(); - - let manifest = try!(desc.download_manifest(cfg)); - - let maybe_url = try!(manifest.package_url("rust", &target_triple, ext)); - - let url = try!(maybe_url.ok_or_else(|| Error::UnsupportedHost(desc.full_spec()))); - - download_and_check(&url, update_hash, ext, cfg) +pub fn download_dist<'a>(toolchain: &str, + update_hash: Option<&Path>, + cfg: DownloadCfg<'a>) + -> Result, String)>> { + let desc = try!(ToolchainDesc::from_str(toolchain).ok_or(Error::InvalidToolchainName)); + + let target_triple = try!(desc.target_triple() + .ok_or_else(|| Error::UnsupportedHost(desc.full_spec()))); + let ext = get_installer_ext(); + + let manifest = try!(desc.download_manifest(cfg)); + + let maybe_url = try!(manifest.package_url("rust", &target_triple, ext)); + + let url = try!(maybe_url.ok_or_else(|| Error::UnsupportedHost(desc.full_spec()))); + + download_and_check(&url, update_hash, ext, cfg) } pub fn get_host_triple() -> (&'static str, Option<&'static str>, Option<&'static str>) { - let arch = match env::consts::ARCH { - "x86" => "i686", // Why, rust... WHY? - other => other, - }; - - let os = match env::consts::OS { - "windows" => Some("pc-windows"), - "linux" => Some("unknown-linux"), - "macos" => Some("apple-darwin"), - _ => None, - }; - - let env = match () { - () if cfg!(target_env = "gnu") => Some("gnu"), - () if cfg!(target_env = "msvc") => Some("msvc"), - _ => None, - }; - - (arch, os, env) + let arch = match env::consts::ARCH { + "x86" => "i686", // Why, rust... WHY? + other => other, + }; + + let os = match env::consts::OS { + "windows" => Some("pc-windows"), + "linux" => Some("unknown-linux"), + "macos" => Some("apple-darwin"), + _ => None, + }; + + let env = match () { + () if cfg!(target_env = "gnu") => Some("gnu"), + () if cfg!(target_env = "msvc") => Some("msvc"), + _ => None, + }; + + (arch, os, env) } pub fn get_installer_ext() -> &'static str { - if cfg!(windows) { - return ".msi" - } - ".tar.gz" + if cfg!(windows) { + return ".msi"; + } + ".tar.gz" } pub fn download_hash(url: &str, cfg: DownloadCfg) -> Result { - let hash_url = try!(utils::parse_url(&(url.to_owned() + ".sha256"))); - let hash_file = try!(cfg.temp_cfg.new_file()); - - try!(utils::download_file(hash_url, &hash_file, None, ntfy!(&cfg.notify_handler))); - - Ok(try!(utils::read_file("hash", &hash_file).map(|s| s[0..64].to_owned()))) + let hash_url = try!(utils::parse_url(&(url.to_owned() + ".sha256"))); + let hash_file = try!(cfg.temp_cfg.new_file()); + + try!(utils::download_file(hash_url, &hash_file, None, ntfy!(&cfg.notify_handler))); + + Ok(try!(utils::read_file("hash", &hash_file).map(|s| s[0..64].to_owned()))) } diff --git a/rust-install/src/env_var.rs b/rust-install/src/env_var.rs index fd72db91f9..a5d506dfc7 100644 --- a/rust-install/src/env_var.rs +++ b/rust-install/src/env_var.rs @@ -6,19 +6,19 @@ use std::process::Command; use utils; pub fn set_default(name: &str, value: &OsStr, cmd: &mut Command) { - let new_value = env::var_os(name) - .and_then(utils::if_not_empty) - .unwrap_or(value.to_owned()); - cmd.env(name, new_value); + let new_value = env::var_os(name) + .and_then(utils::if_not_empty) + .unwrap_or(value.to_owned()); + cmd.env(name, new_value); } pub fn set_path(name: &str, value: &Path, cmd: &mut Command) { - let old_value = env::var_os(name); - let mut parts = vec![value.to_owned()]; - if let Some(ref v) = old_value { - parts.extend(env::split_paths(v)); - } - let new_value = env::join_paths(parts).unwrap_or_else(|_| OsString::from(value)); - - cmd.env(name, new_value); + let old_value = env::var_os(name); + let mut parts = vec![value.to_owned()]; + if let Some(ref v) = old_value { + parts.extend(env::split_paths(v)); + } + let new_value = env::join_paths(parts).unwrap_or_else(|_| OsString::from(value)); + + cmd.env(name, new_value); } diff --git a/rust-install/src/errors.rs b/rust-install/src/errors.rs index 3e20801e6a..3d5883dd54 100644 --- a/rust-install/src/errors.rs +++ b/rust-install/src/errors.rs @@ -7,27 +7,31 @@ use utils; use notify::{NotificationLevel, Notifyable}; pub enum Notification<'a> { - Utils(utils::Notification<'a>), - Temp(temp::Notification<'a>), - - Extracting(&'a Path, &'a Path), - UpdateHashMatches(&'a str), - CantReadUpdateHash(&'a Path), - NoUpdateHash(&'a Path), - ChecksumValid(&'a str), + Utils(utils::Notification<'a>), + Temp(temp::Notification<'a>), + + Extracting(&'a Path, &'a Path), + UpdateHashMatches(&'a str), + CantReadUpdateHash(&'a Path), + NoUpdateHash(&'a Path), + ChecksumValid(&'a str), } pub enum Error { - Utils(utils::Error), - Temp(temp::Error), - - InvalidFileExtension, - InvalidInstaller, - InvalidToolchainName, - NotInstalledHere, - InstallTypeNotPossible, - UnsupportedHost(String), - ChecksumFailed { url: String, expected: String, calculated: String }, + Utils(utils::Error), + Temp(temp::Error), + + InvalidFileExtension, + InvalidInstaller, + InvalidToolchainName, + NotInstalledHere, + InstallTypeNotPossible, + UnsupportedHost(String), + ChecksumFailed { + url: String, + expected: String, + calculated: String, + }, } pub type Result = ::std::result::Result; @@ -40,57 +44,56 @@ extend_notification!(Notification: utils::Notification, n => Notification::Utils extend_notification!(Notification: temp::Notification, n => Notification::Temp(n)); impl<'a> Notification<'a> { - pub fn level(&self) -> NotificationLevel { - use self::Notification::*; - match *self { - Temp(ref n) => n.level(), - Utils(ref n) => n.level(), - NoUpdateHash(_) => - NotificationLevel::Verbose, - Extracting(_, _) | ChecksumValid(_) => - NotificationLevel::Normal, - UpdateHashMatches(_) => - NotificationLevel::Info, - CantReadUpdateHash(_) => - NotificationLevel::Warn, - } - } + pub fn level(&self) -> NotificationLevel { + use self::Notification::*; + match *self { + Temp(ref n) => n.level(), + Utils(ref n) => n.level(), + NoUpdateHash(_) => NotificationLevel::Verbose, + Extracting(_, _) | ChecksumValid(_) => NotificationLevel::Normal, + UpdateHashMatches(_) => NotificationLevel::Info, + CantReadUpdateHash(_) => NotificationLevel::Warn, + } + } } impl<'a> Display for Notification<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> { - use self::Notification::*; - match *self { - Temp(ref n) => n.fmt(f), - Utils(ref n) => n.fmt(f), - Extracting(_, _) => - write!(f, "extracting..."), - UpdateHashMatches(hash) => - write!(f, "update hash matches: {}, skipping update...", hash), - CantReadUpdateHash(path) => - write!(f, "can't read update hash file: '{}', can't skip update...", path.display()), - NoUpdateHash(path) => - write!(f, "no update hash at: '{}'", path.display()), - ChecksumValid(_) => - write!(f, "checksum passed"), - } - } + fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> { + use self::Notification::*; + match *self { + Temp(ref n) => n.fmt(f), + Utils(ref n) => n.fmt(f), + Extracting(_, _) => write!(f, "extracting..."), + UpdateHashMatches(hash) => + write!(f, "update hash matches: {}, skipping update...", hash), + CantReadUpdateHash(path) => + write!(f, + "can't read update hash file: '{}', can't skip update...", + path.display()), + NoUpdateHash(path) => write!(f, "no update hash at: '{}'", path.display()), + ChecksumValid(_) => write!(f, "checksum passed"), + } + } } impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> { - use self::Error::*; - match *self { - Temp(ref n) => n.fmt(f), - Utils(ref n) => n.fmt(f), - InvalidFileExtension => write!(f, "invalid file extension"), - InvalidInstaller => write!(f, "invalid installer"), - InvalidToolchainName => write!(f, "invalid custom toolchain name"), - NotInstalledHere => write!(f, "not installed here"), - InstallTypeNotPossible => write!(f, "install type not possible"), - UnsupportedHost(ref spec) => write!(f, "a binary package was not provided for: '{}'", spec), - ChecksumFailed { url: _, ref expected, ref calculated } => - write!(f, "checksum failed, expected: '{}', calculated: '{}'", expected, calculated), - } - } + fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> { + use self::Error::*; + match *self { + Temp(ref n) => n.fmt(f), + Utils(ref n) => n.fmt(f), + InvalidFileExtension => write!(f, "invalid file extension"), + InvalidInstaller => write!(f, "invalid installer"), + InvalidToolchainName => write!(f, "invalid custom toolchain name"), + NotInstalledHere => write!(f, "not installed here"), + InstallTypeNotPossible => write!(f, "install type not possible"), + UnsupportedHost(ref spec) => + write!(f, "a binary package was not provided for: '{}'", spec), + ChecksumFailed { url: _, ref expected, ref calculated } => + write!(f, + "checksum failed, expected: '{}', calculated: '{}'", + expected, + calculated), + } + } } diff --git a/rust-install/src/install.rs b/rust-install/src/install.rs index 11bc5e1e30..4029a19ac1 100644 --- a/rust-install/src/install.rs +++ b/rust-install/src/install.rs @@ -19,322 +19,325 @@ const REL_MANIFEST_DIR: &'static str = "lib/rustlib"; const REL_MANIFEST_DIR: &'static str = "bin\\rustlib"; pub struct InstallPrefix { - path: PathBuf, - install_type: InstallType, + path: PathBuf, + install_type: InstallType, } #[derive(Eq, PartialEq, Copy, Clone)] pub enum InstallType { - // Must be uninstalled by deleting the entire directory - Owned, - // Must be uninstalled via `uninstall.sh` on linux or `msiexec /x` on windows - Shared, + // Must be uninstalled by deleting the entire directory + Owned, + // Must be uninstalled via `uninstall.sh` on linux or `msiexec /x` on windows + Shared, } pub enum Uninstaller { - Sh(PathBuf), - Msi(String), + Sh(PathBuf), + Msi(String), } impl Uninstaller { - pub fn run(&self) -> Result<()> { - match *self { - Uninstaller::Sh(ref path) => { - Ok(try!( - utils::cmd_status("uninstall.sh", Command::new("sudo").arg("sh").arg(path)) - )) - }, - Uninstaller::Msi(ref id) => { - Ok(try!( - utils::cmd_status("msiexec", Command::new("msiexec").arg("/x").arg(id)) - )) - }, - } - } + pub fn run(&self) -> Result<()> { + match *self { + Uninstaller::Sh(ref path) => { + Ok(try!(utils::cmd_status("uninstall.sh", + Command::new("sudo").arg("sh").arg(path)))) + } + Uninstaller::Msi(ref id) => { + Ok(try!(utils::cmd_status("msiexec", Command::new("msiexec").arg("/x").arg(id)))) + } + } + } } pub enum InstallMethod<'a> { - Copy(&'a Path), - Link(&'a Path), - Installer(&'a Path, &'a temp::Cfg), - Dist(&'a str, Option<&'a Path>, dist::DownloadCfg<'a>), + Copy(&'a Path), + Link(&'a Path), + Installer(&'a Path, &'a temp::Cfg), + Dist(&'a str, Option<&'a Path>, dist::DownloadCfg<'a>), } impl<'a> InstallMethod<'a> { - pub fn install_type_possible(&self, install_type: InstallType) -> bool { - match *self { - InstallMethod::Copy(_)|InstallMethod::Link(_) => install_type == InstallType::Owned, - InstallMethod::Installer(_, _)|InstallMethod::Dist(_,_,_) => true, - } - } - pub fn run(self, prefix: &InstallPrefix, notify_handler: NotifyHandler) -> Result<()> { - if prefix.is_installed_here() { - // Don't uninstall first for Dist method - match self { - InstallMethod::Dist(_,_,_) => {}, - _ => { try!(prefix.uninstall(notify_handler)); }, - } - } - - if !self.install_type_possible(prefix.install_type) { - return Err(Error::InstallTypeNotPossible); - } - - match self { - InstallMethod::Copy(src) => { - try!(utils::copy_dir(src, &prefix.path, ntfy!(¬ify_handler))); - Ok(()) - }, - InstallMethod::Link(src) => { - try!(utils::symlink_dir(src, &prefix.path, ntfy!(¬ify_handler))); - Ok(()) - }, - InstallMethod::Installer(src, temp_cfg) => { - notify_handler.call(Notification::Extracting(src, prefix.path())); - let temp_dir = try!(temp_cfg.new_directory()); - match src.extension().and_then(OsStr::to_str) { - Some("gz") => InstallMethod::tar_gz(src, &temp_dir, prefix), - Some("msi") => InstallMethod::msi(src, &temp_dir, prefix), - _ => Err(Error::InvalidFileExtension), - } - }, - InstallMethod::Dist(toolchain, update_hash, dl_cfg) => { - if let Some((installer, hash)) = try!(dist::download_dist(toolchain, update_hash, dl_cfg)) { - try!(InstallMethod::Installer(&*installer, dl_cfg.temp_cfg).run(prefix, dl_cfg.notify_handler)); - if let Some(hash_file) = update_hash { - try!(utils::write_file("update hash", hash_file, &hash)); - } - } - Ok(()) - } - } - } - - fn tar_gz(src: &Path, work_dir: &Path, prefix: &InstallPrefix) -> Result<()> { - let installer_dir = Path::new(try!(Path::new(src.file_stem().unwrap()).file_stem() - .ok_or(Error::InvalidFileExtension))); - - try!(utils::cmd_status("tar", - Command::new("tar") - .arg("xzf").arg(src) - .arg("-C").arg(work_dir) - )); - - // Find the root Rust folder within the subfolder - let root_dir = try!(try!(utils::read_dir("install", work_dir)) - .filter_map(io::Result::ok) - .map(|e| e.path()) - .filter(|p| utils::is_directory(&p)) - .next() - .ok_or(Error::InvalidInstaller)); - - let mut cmd = Command::new("sh"); - let mut arg = OsString::from("--prefix=\""); - arg.push(&prefix.path); - arg.push("\""); - cmd - .arg(root_dir.join("install.sh")) - .arg(arg); - - if prefix.install_type != InstallType::Shared { - cmd.arg("--disable-ldconfig"); - } - - let result = Ok(try!(utils::cmd_status("sh", &mut cmd))); - - let _ = fs::remove_dir_all(&installer_dir); - - if result.is_err() && prefix.install_type == InstallType::Owned { - let _ = fs::remove_dir_all(&prefix.path); - } - - result - } - fn msi(src: &Path, work_dir: &Path, prefix: &InstallPrefix) -> Result<()> { - let msi_owned = || -> Result<()> { - let target_arg = utils::prefix_arg("TARGETDIR=", work_dir); - - // Extract the MSI to the subfolder - let mut cmd = Command::new("msiexec"); - cmd - .arg("/a").arg(src) - .arg("/qn") - .arg(&target_arg); - - try!(utils::cmd_status("msiexec", - Command::new("msiexec") - .arg("/a").arg(src) - .arg("/qn") - .arg(&target_arg) - )); - - // Find the root Rust folder within the subfolder - let root_dir = try!(try!(utils::read_dir("install", work_dir)) - .filter_map(io::Result::ok) - .map(|e| e.path()) - .filter(|p| utils::is_directory(&p)) - .next() - .ok_or(Error::InvalidInstaller)); - - // Rename and move it to the toolchain directory - Ok(try!(utils::rename_dir("install", &root_dir, &prefix.path))) - }; - - let msi_shared = || -> Result<()> { - let target_arg = utils::prefix_arg("TARGETDIR=", &prefix.path); - - // Extract the MSI to the subfolder - Ok(try!(utils::cmd_status("msiexec", - Command::new("msiexec") - .arg("/i").arg(src) - .arg("/qn") - .arg(&target_arg) - ))) - }; - - match prefix.install_type { - InstallType::Owned => { - let result = msi_owned(); - if result.is_err() { - let _ = fs::remove_dir_all(&prefix.path); - } - result - }, - InstallType::Shared => { - msi_shared() - } - } - } + pub fn install_type_possible(&self, install_type: InstallType) -> bool { + match *self { + InstallMethod::Copy(_) | InstallMethod::Link(_) => install_type == InstallType::Owned, + InstallMethod::Installer(_, _) | InstallMethod::Dist(_, _, _) => true, + } + } + pub fn run(self, prefix: &InstallPrefix, notify_handler: NotifyHandler) -> Result<()> { + if prefix.is_installed_here() { + // Don't uninstall first for Dist method + match self { + InstallMethod::Dist(_, _, _) => {} + _ => { + try!(prefix.uninstall(notify_handler)); + } + } + } + + if !self.install_type_possible(prefix.install_type) { + return Err(Error::InstallTypeNotPossible); + } + + match self { + InstallMethod::Copy(src) => { + try!(utils::copy_dir(src, &prefix.path, ntfy!(¬ify_handler))); + Ok(()) + } + InstallMethod::Link(src) => { + try!(utils::symlink_dir(src, &prefix.path, ntfy!(¬ify_handler))); + Ok(()) + } + InstallMethod::Installer(src, temp_cfg) => { + notify_handler.call(Notification::Extracting(src, prefix.path())); + let temp_dir = try!(temp_cfg.new_directory()); + match src.extension().and_then(OsStr::to_str) { + Some("gz") => InstallMethod::tar_gz(src, &temp_dir, prefix), + Some("msi") => InstallMethod::msi(src, &temp_dir, prefix), + _ => Err(Error::InvalidFileExtension), + } + } + InstallMethod::Dist(toolchain, update_hash, dl_cfg) => { + if let Some((installer, hash)) = try!(dist::download_dist(toolchain, + update_hash, + dl_cfg)) { + try!(InstallMethod::Installer(&*installer, dl_cfg.temp_cfg) + .run(prefix, dl_cfg.notify_handler)); + if let Some(hash_file) = update_hash { + try!(utils::write_file("update hash", hash_file, &hash)); + } + } + Ok(()) + } + } + } + + fn tar_gz(src: &Path, work_dir: &Path, prefix: &InstallPrefix) -> Result<()> { + let installer_dir = Path::new(try!(Path::new(src.file_stem().unwrap()) + .file_stem() + .ok_or(Error::InvalidFileExtension))); + + try!(utils::cmd_status("tar", + Command::new("tar") + .arg("xzf") + .arg(src) + .arg("-C") + .arg(work_dir))); + + // Find the root Rust folder within the subfolder + let root_dir = try!(try!(utils::read_dir("install", work_dir)) + .filter_map(io::Result::ok) + .map(|e| e.path()) + .filter(|p| utils::is_directory(&p)) + .next() + .ok_or(Error::InvalidInstaller)); + + let mut cmd = Command::new("sh"); + let mut arg = OsString::from("--prefix=\""); + arg.push(&prefix.path); + arg.push("\""); + cmd.arg(root_dir.join("install.sh")) + .arg(arg); + + if prefix.install_type != InstallType::Shared { + cmd.arg("--disable-ldconfig"); + } + + let result = Ok(try!(utils::cmd_status("sh", &mut cmd))); + + let _ = fs::remove_dir_all(&installer_dir); + + if result.is_err() && prefix.install_type == InstallType::Owned { + let _ = fs::remove_dir_all(&prefix.path); + } + + result + } + fn msi(src: &Path, work_dir: &Path, prefix: &InstallPrefix) -> Result<()> { + let msi_owned = || -> Result<()> { + let target_arg = utils::prefix_arg("TARGETDIR=", work_dir); + + // Extract the MSI to the subfolder + let mut cmd = Command::new("msiexec"); + cmd.arg("/a") + .arg(src) + .arg("/qn") + .arg(&target_arg); + + try!(utils::cmd_status("msiexec", + Command::new("msiexec") + .arg("/a") + .arg(src) + .arg("/qn") + .arg(&target_arg))); + + // Find the root Rust folder within the subfolder + let root_dir = try!(try!(utils::read_dir("install", work_dir)) + .filter_map(io::Result::ok) + .map(|e| e.path()) + .filter(|p| utils::is_directory(&p)) + .next() + .ok_or(Error::InvalidInstaller)); + + // Rename and move it to the toolchain directory + Ok(try!(utils::rename_dir("install", &root_dir, &prefix.path))) + }; + + let msi_shared = || -> Result<()> { + let target_arg = utils::prefix_arg("TARGETDIR=", &prefix.path); + + // Extract the MSI to the subfolder + Ok(try!(utils::cmd_status("msiexec", + Command::new("msiexec") + .arg("/i") + .arg(src) + .arg("/qn") + .arg(&target_arg)))) + }; + + match prefix.install_type { + InstallType::Owned => { + let result = msi_owned(); + if result.is_err() { + let _ = fs::remove_dir_all(&prefix.path); + } + result + } + InstallType::Shared => { + msi_shared() + } + } + } } pub fn bin_path(name: &str) -> PathBuf { - let mut path = PathBuf::from("bin"); - path.push(name.to_owned() + env::consts::EXE_SUFFIX); - path + let mut path = PathBuf::from("bin"); + path.push(name.to_owned() + env::consts::EXE_SUFFIX); + path } impl InstallPrefix { - pub fn from(path: PathBuf, install_type: InstallType) -> Self { - InstallPrefix { - path: path, - install_type: install_type, - } - } - pub fn path(&self) -> &Path { - &self.path - } - pub fn manifest_dir(&self) -> PathBuf { - let mut path = self.path.clone(); - path.push(REL_MANIFEST_DIR); - path - } - pub fn manifest_file(&self, name: &str) -> PathBuf { - let mut path = self.manifest_dir(); - path.push(name); - path - } - pub fn binary_file(&self, name: &str) -> PathBuf { - let mut path = self.path.clone(); - path.push(bin_path(name)); - path - } - pub fn doc_path(&self, relative: &str) -> Result { - let parts = vec!["share", "doc", "rust", "html"]; - let mut doc_dir = self.path.clone(); - for part in parts { - doc_dir.push(part); - } - doc_dir.push(relative); - - Ok(doc_dir) - } - pub fn is_installed_here(&self) -> bool { - match self.install_type { - InstallType::Owned => utils::is_directory(&self.path), - InstallType::Shared => utils::is_directory(&self.manifest_dir()), - } - } - pub fn get_uninstall_sh(&self) -> Option { - let path = self.manifest_file("uninstall.sh"); - if utils::is_file(&path) { - Some(path) - } else { - None - } - } - - #[cfg(windows)] - pub fn get_uninstall_msi(&self, notify_handler: NotifyHandler) -> Option { - let canon_path = utils::canonicalize_path(&self.path, ntfy!(¬ify_handler)); - - if let Ok(installers) = msi::all_installers() { - for installer in &installers { - if let Ok(loc) = installer.install_location() { - let path = utils::canonicalize_path(&loc, ntfy!(¬ify_handler)); - - if path == canon_path { - return Some(installer.product_id().to_owned()); - } - } - } - } - - None - } - #[cfg(not(windows))] - pub fn get_uninstall_msi(&self, _: NotifyHandler) -> Option { - None - } - pub fn get_uninstaller(&self, notify_handler: NotifyHandler) -> Option { - self.get_uninstall_sh().map(Uninstaller::Sh).or_else( - || self.get_uninstall_msi(notify_handler).map(Uninstaller::Msi) - ) - } - pub fn uninstall(&self, notify_handler: NotifyHandler) -> Result<()> { - if self.is_installed_here() { - match self.install_type { - InstallType::Owned => { - Ok(try!(utils::remove_dir("install", &self.path, ntfy!(¬ify_handler)))) - }, - InstallType::Shared => { - if let Some(uninstaller) = self.get_uninstaller(notify_handler) { - uninstaller.run() - } else { - Err(Error::NotInstalledHere) - } - }, - } - } else { - Err(Error::NotInstalledHere) - } - } - pub fn install(&self, method: InstallMethod, notify_handler: NotifyHandler) -> Result<()> { - method.run(self, notify_handler) - } - - pub fn set_ldpath(&self, cmd: &mut Command) { - let new_path = self.path.join("lib"); - - env_var::set_path("LD_LIBRARY_PATH", &new_path, cmd); - env_var::set_path("DYLD_LIBRARY_PATH", &new_path, cmd); - } - - pub fn set_env(&self, cmd: &mut Command) { - let cargo_path = self.path.join("cargo"); - - self.set_ldpath(cmd); - env_var::set_default("CARGO_HOME", cargo_path.as_ref(), cmd); - } - - pub fn create_command(&self, binary: &str) -> Command { - let binary_path = self.binary_file(binary); - let mut cmd = Command::new(binary_path); - - self.set_env(&mut cmd); - cmd - } - - pub fn open_docs(&self, relative: &str) -> Result<()> { - Ok(try!(utils::open_browser(&try!(self.doc_path(relative))))) - } + pub fn from(path: PathBuf, install_type: InstallType) -> Self { + InstallPrefix { + path: path, + install_type: install_type, + } + } + pub fn path(&self) -> &Path { + &self.path + } + pub fn manifest_dir(&self) -> PathBuf { + let mut path = self.path.clone(); + path.push(REL_MANIFEST_DIR); + path + } + pub fn manifest_file(&self, name: &str) -> PathBuf { + let mut path = self.manifest_dir(); + path.push(name); + path + } + pub fn binary_file(&self, name: &str) -> PathBuf { + let mut path = self.path.clone(); + path.push(bin_path(name)); + path + } + pub fn doc_path(&self, relative: &str) -> Result { + let parts = vec!["share", "doc", "rust", "html"]; + let mut doc_dir = self.path.clone(); + for part in parts { + doc_dir.push(part); + } + doc_dir.push(relative); + + Ok(doc_dir) + } + pub fn is_installed_here(&self) -> bool { + match self.install_type { + InstallType::Owned => utils::is_directory(&self.path), + InstallType::Shared => utils::is_directory(&self.manifest_dir()), + } + } + pub fn get_uninstall_sh(&self) -> Option { + let path = self.manifest_file("uninstall.sh"); + if utils::is_file(&path) { + Some(path) + } else { + None + } + } + + #[cfg(windows)] + pub fn get_uninstall_msi(&self, notify_handler: NotifyHandler) -> Option { + let canon_path = utils::canonicalize_path(&self.path, ntfy!(¬ify_handler)); + + if let Ok(installers) = msi::all_installers() { + for installer in &installers { + if let Ok(loc) = installer.install_location() { + let path = utils::canonicalize_path(&loc, ntfy!(¬ify_handler)); + + if path == canon_path { + return Some(installer.product_id().to_owned()); + } + } + } + } + + None + } + #[cfg(not(windows))] + pub fn get_uninstall_msi(&self, _: NotifyHandler) -> Option { + None + } + pub fn get_uninstaller(&self, notify_handler: NotifyHandler) -> Option { + self.get_uninstall_sh() + .map(Uninstaller::Sh) + .or_else(|| self.get_uninstall_msi(notify_handler).map(Uninstaller::Msi)) + } + pub fn uninstall(&self, notify_handler: NotifyHandler) -> Result<()> { + if self.is_installed_here() { + match self.install_type { + InstallType::Owned => { + Ok(try!(utils::remove_dir("install", &self.path, ntfy!(¬ify_handler)))) + } + InstallType::Shared => { + if let Some(uninstaller) = self.get_uninstaller(notify_handler) { + uninstaller.run() + } else { + Err(Error::NotInstalledHere) + } + } + } + } else { + Err(Error::NotInstalledHere) + } + } + pub fn install(&self, method: InstallMethod, notify_handler: NotifyHandler) -> Result<()> { + method.run(self, notify_handler) + } + + pub fn set_ldpath(&self, cmd: &mut Command) { + let new_path = self.path.join("lib"); + + env_var::set_path("LD_LIBRARY_PATH", &new_path, cmd); + env_var::set_path("DYLD_LIBRARY_PATH", &new_path, cmd); + } + + pub fn set_env(&self, cmd: &mut Command) { + let cargo_path = self.path.join("cargo"); + + self.set_ldpath(cmd); + env_var::set_default("CARGO_HOME", cargo_path.as_ref(), cmd); + } + + pub fn create_command(&self, binary: &str) -> Command { + let binary_path = self.binary_file(binary); + let mut cmd = Command::new(binary_path); + + self.set_env(&mut cmd); + cmd + } + + pub fn open_docs(&self, relative: &str) -> Result<()> { + Ok(try!(utils::open_browser(&try!(self.doc_path(relative))))) + } } diff --git a/rust-install/src/msi.rs b/rust-install/src/msi.rs index b62fb867c8..3f1a3100d6 100644 --- a/rust-install/src/msi.rs +++ b/rust-install/src/msi.rs @@ -11,135 +11,137 @@ pub struct Installers(RegKey); pub struct AllInstallers(Installers, Installers); pub struct Installer { - id: String, - key: RegKey, + id: String, + key: RegKey, } impl Installer { - pub fn product_id(&self) -> &str { - &self.id - } - pub fn comments(&self) -> io::Result { - self.key.get_value("Comments") - } - pub fn display_name(&self) -> io::Result { - self.key.get_value("DisplayName") - } - pub fn display_version(&self) -> io::Result { - self.key.get_value("DisplayVersion") - } - pub fn estimated_size_kb(&self) -> io::Result { - self.key.get_value("EstimatedSize") - } - pub fn install_date(&self) -> io::Result { - self.key.get_value("InstallDate") - } - pub fn install_location(&self) -> io::Result { - self.key.get_value("InstallLocation").map(|s: String| s.into()) - } - pub fn install_source(&self) -> io::Result { - self.key.get_value("InstallSource").map(|s: String| s.into()) - } - pub fn language(&self) -> io::Result { - self.key.get_value("Language") - } - pub fn publisher(&self) -> io::Result { - self.key.get_value("Publisher") - } - pub fn url_info_about(&self) -> io::Result { - self.key.get_value("UrlInfoAbout") - } - pub fn version(&self) -> io::Result { - self.key.get_value("Version") - } - pub fn version_major(&self) -> io::Result { - self.key.get_value("VersionMajor") - } - pub fn version_minor(&self) -> io::Result { - self.key.get_value("VersionMinor") - } - pub fn system_component(&self) -> bool { - if let Ok(1u32) = self.key.get_value("SystemComponent") { - true - } else { - false - } - } + pub fn product_id(&self) -> &str { + &self.id + } + pub fn comments(&self) -> io::Result { + self.key.get_value("Comments") + } + pub fn display_name(&self) -> io::Result { + self.key.get_value("DisplayName") + } + pub fn display_version(&self) -> io::Result { + self.key.get_value("DisplayVersion") + } + pub fn estimated_size_kb(&self) -> io::Result { + self.key.get_value("EstimatedSize") + } + pub fn install_date(&self) -> io::Result { + self.key.get_value("InstallDate") + } + pub fn install_location(&self) -> io::Result { + self.key.get_value("InstallLocation").map(|s: String| s.into()) + } + pub fn install_source(&self) -> io::Result { + self.key.get_value("InstallSource").map(|s: String| s.into()) + } + pub fn language(&self) -> io::Result { + self.key.get_value("Language") + } + pub fn publisher(&self) -> io::Result { + self.key.get_value("Publisher") + } + pub fn url_info_about(&self) -> io::Result { + self.key.get_value("UrlInfoAbout") + } + pub fn version(&self) -> io::Result { + self.key.get_value("Version") + } + pub fn version_major(&self) -> io::Result { + self.key.get_value("VersionMajor") + } + pub fn version_minor(&self) -> io::Result { + self.key.get_value("VersionMinor") + } + pub fn system_component(&self) -> bool { + if let Ok(1u32) = self.key.get_value("SystemComponent") { + true + } else { + false + } + } } impl<'a> IntoIterator for &'a Installers { - type IntoIter = InstallerIter<'a>; - type Item = Installer; - - fn into_iter(self) -> InstallerIter<'a> { - self.iter() - } + type IntoIter = InstallerIter<'a>; + type Item = Installer; + + fn into_iter(self) -> InstallerIter<'a> { + self.iter() + } } impl AllInstallers { - pub fn iter(&self) -> iter::Chain { - self.0.iter().chain(self.1.iter()) - } + pub fn iter(&self) -> iter::Chain { + self.0.iter().chain(self.1.iter()) + } } impl<'a> IntoIterator for &'a AllInstallers { - type IntoIter = iter::Chain, InstallerIter<'a>>; - type Item = Installer; - - fn into_iter(self) -> iter::Chain, InstallerIter<'a>> { - self.iter() - } + type IntoIter = iter::Chain, InstallerIter<'a>>; + type Item = Installer; + + fn into_iter(self) -> iter::Chain, InstallerIter<'a>> { + self.iter() + } } impl Installers { - pub fn iter(&self) -> InstallerIter { - InstallerIter(&self.0, self.0.enum_keys()) - } + pub fn iter(&self) -> InstallerIter { + InstallerIter(&self.0, self.0.enum_keys()) + } } pub struct InstallerIter<'a>(&'a RegKey, EnumKeys<'a>); impl<'a> Iterator for InstallerIter<'a> { - type Item = Installer; - - fn next(&mut self) -> Option { - loop { - let n = self.1.next(); - - if let Some(result) = n { - if let Ok(name) = result { - if let Ok(key) = self.0.open_subkey_with_flags(&name, KEY_READ) { - if let Ok(1u32) = key.get_value("WindowsInstaller") { - return Some(Installer { id: name, key: key }); - } - } - } - } else { - return None; - } - } - } + type Item = Installer; + + fn next(&mut self) -> Option { + loop { + let n = self.1.next(); + + if let Some(result) = n { + if let Ok(name) = result { + if let Ok(key) = self.0.open_subkey_with_flags(&name, KEY_READ) { + if let Ok(1u32) = key.get_value("WindowsInstaller") { + return Some(Installer { + id: name, + key: key, + }); + } + } + } + } else { + return None; + } + } + } } fn read_installer_registry(key: HKEY) -> io::Result { - let root = RegKey::predef(key); - let uninstall = try!(root.open_subkey_with_flags("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall", KEY_READ)); - - Ok(Installers(uninstall)) + let root = RegKey::predef(key); + let uninstall = try!(root.open_subkey_with_flags("SOFTWARE\\Microsoft\\Windows\\CurrentVersi\ + on\\Uninstall", + KEY_READ)); + + Ok(Installers(uninstall)) } pub fn local_machine_installers() -> io::Result { - read_installer_registry(HKEY_LOCAL_MACHINE) + read_installer_registry(HKEY_LOCAL_MACHINE) } pub fn current_user_installers() -> io::Result { - read_installer_registry(HKEY_CURRENT_USER) + read_installer_registry(HKEY_CURRENT_USER) } pub fn all_installers() -> io::Result { - Ok(AllInstallers( - try!(local_machine_installers()), - try!(current_user_installers()) - )) + Ok(AllInstallers(try!(local_machine_installers()), + try!(current_user_installers()))) } - diff --git a/rust-install/src/notify.rs b/rust-install/src/notify.rs index 24bb764445..181dd1b5f1 100644 --- a/rust-install/src/notify.rs +++ b/rust-install/src/notify.rs @@ -2,13 +2,13 @@ use std::sync::Arc; #[fundamental] pub trait Notifyable { - fn call(&self, n: N); + fn call(&self, n: N); } impl Notifyable for F { - fn call(&self, n: N) { - self(n) - } + fn call(&self, n: N) { + self(n) + } } #[derive(Debug)] @@ -17,9 +17,9 @@ pub struct NotifyHandler<'a, T: 'a + ?Sized>(Option<&'a T>); impl<'a, T: 'a + ?Sized> Copy for NotifyHandler<'a, T> { } impl<'a, T: 'a + ?Sized> Clone for NotifyHandler<'a, T> { - fn clone(&self) -> Self { - *self - } + fn clone(&self) -> Self { + *self + } } #[derive(Debug)] @@ -27,100 +27,104 @@ impl<'a, T: 'a + ?Sized> Clone for NotifyHandler<'a, T> { pub struct SharedNotifyHandler(Option>); impl Clone for SharedNotifyHandler { - fn clone(&self) -> Self { - SharedNotifyHandler(self.0.clone()) - } + fn clone(&self) -> Self { + SharedNotifyHandler(self.0.clone()) + } } impl<'a, T: 'a + ?Sized> NotifyHandler<'a, T> { - pub fn some(arg: &'a T) -> Self { - NotifyHandler(Some(arg)) - } - pub fn none() -> Self { - NotifyHandler(None) - } - pub fn call(&self, arg: U) where T: Notifyable { - if let Some(f) = self.0 { - f.call(arg); - } - } + pub fn some(arg: &'a T) -> Self { + NotifyHandler(Some(arg)) + } + pub fn none() -> Self { + NotifyHandler(None) + } + pub fn call(&self, arg: U) + where T: Notifyable + { + if let Some(f) = self.0 { + f.call(arg); + } + } } impl SharedNotifyHandler { - pub fn some(arg: Arc) -> Self { - SharedNotifyHandler(Some(arg)) - } - pub fn none() -> Self { - SharedNotifyHandler(None) - } - pub fn as_ref<'a>(&'a self) -> NotifyHandler<'a, T> { - match self.0 { - Some(ref f) => NotifyHandler(Some(f)), - None => NotifyHandler(None), - } - } - pub fn call(&self, arg: U) where T: Notifyable { - self.as_ref().call(arg) - } + pub fn some(arg: Arc) -> Self { + SharedNotifyHandler(Some(arg)) + } + pub fn none() -> Self { + SharedNotifyHandler(None) + } + pub fn as_ref<'a>(&'a self) -> NotifyHandler<'a, T> { + match self.0 { + Some(ref f) => NotifyHandler(Some(f)), + None => NotifyHandler(None), + } + } + pub fn call(&self, arg: U) + where T: Notifyable + { + self.as_ref().call(arg) + } } #[derive(Debug)] pub enum NotificationLevel { - Verbose, - Normal, - Info, - Warn, - Error, + Verbose, + Normal, + Info, + Warn, + Error, } #[macro_export] macro_rules! extend_error { - ($cur:ty: $base:ty, $p:ident => $e:expr) => ( - impl From<$base> for $cur { - fn from($p: $base) -> $cur { - $e - } - } - ) + ($cur:ty: $base:ty, $p:ident => $e:expr) => ( + impl From<$base> for $cur { + fn from($p: $base) -> $cur { + $e + } + } + ) } #[macro_export] macro_rules! extend_notification { - ($( $cur:ident )::*: $( $base:ident )::*, $p:ident => $e:expr) => ( - impl<'a, 'b> $crate::notify::Notifyable<$($base)::*<'a>> for $crate::notify::NotifyHandler<'b, for<'c> $crate::notify::Notifyable<$($cur)::*<'c>>> { - fn call(&self, $p: $($base)::*<'a>) { - self.call($e) - } - } - impl<'a> $crate::notify::Notifyable<$($base)::*<'a>> for $crate::notify::SharedNotifyHandler $crate::notify::Notifyable<$($cur)::*<'b>>> { - fn call(&self, $p: $($base)::*<'a>) { - self.call($e) - } - } - ) + ($( $cur:ident )::*: $( $base:ident )::*, $p:ident => $e:expr) => ( + impl<'a, 'b> $crate::notify::Notifyable<$($base)::*<'a>> for $crate::notify::NotifyHandler<'b, for<'c> $crate::notify::Notifyable<$($cur)::*<'c>>> { + fn call(&self, $p: $($base)::*<'a>) { + self.call($e) + } + } + impl<'a> $crate::notify::Notifyable<$($base)::*<'a>> for $crate::notify::SharedNotifyHandler $crate::notify::Notifyable<$($cur)::*<'b>>> { + fn call(&self, $p: $($base)::*<'a>) { + self.call($e) + } + } + ) } #[macro_export] macro_rules! declare_notification { - ($( $cur:ident )::*: $( $base:ident )::*, $p:ident => $e:expr) => ( - impl<'a, 'b> $crate::notify::Notifyable<$($base)::*<'a>> for $crate::notify::NotifyHandler<'b, for<'c> $crate::notify::Notifyable<$($cur)::*<'c>>> { - fn call(&self, $p: $($base)::*<'a>) { - self.call($e) - } - } - impl<'a> $crate::notify::Notifyable<$($base)::*<'a>> for $crate::notify::SharedNotifyHandler $crate::notify::Notifyable<$($cur)::*<'b>>> { - fn call(&self, $p: $($base)::*<'a>) { - self.call($e) - } - } - ) + ($( $cur:ident )::*: $( $base:ident )::*, $p:ident => $e:expr) => ( + impl<'a, 'b> $crate::notify::Notifyable<$($base)::*<'a>> for $crate::notify::NotifyHandler<'b, for<'c> $crate::notify::Notifyable<$($cur)::*<'c>>> { + fn call(&self, $p: $($base)::*<'a>) { + self.call($e) + } + } + impl<'a> $crate::notify::Notifyable<$($base)::*<'a>> for $crate::notify::SharedNotifyHandler $crate::notify::Notifyable<$($cur)::*<'b>>> { + fn call(&self, $p: $($base)::*<'a>) { + self.call($e) + } + } + ) } #[macro_export] macro_rules! ntfy { - ($e:expr) => ( - $crate::notify::NotifyHandler::some($e) - ) + ($e:expr) => ( + $crate::notify::NotifyHandler::some($e) + ) } #[macro_export] macro_rules! shared_ntfy { - ($e:expr) => ( - $crate::notify::SharedNotifyHandler::some(::std::sync::Arc::new($e)) - ) -} \ No newline at end of file + ($e:expr) => ( + $crate::notify::SharedNotifyHandler::some(::std::sync::Arc::new($e)) + ) +} diff --git a/rust-install/src/temp.rs b/rust-install/src/temp.rs index a77e3cf375..b1f3ad9287 100644 --- a/rust-install/src/temp.rs +++ b/rust-install/src/temp.rs @@ -8,9 +8,18 @@ use utils::raw; use notify::{self, NotificationLevel, Notifyable}; pub enum Error { - CreatingRoot { path: PathBuf, error: io::Error }, - CreatingFile { path: PathBuf, error: io::Error }, - CreatingDirectory { path: PathBuf, error: io::Error }, + CreatingRoot { + path: PathBuf, + error: io::Error, + }, + CreatingFile { + path: PathBuf, + error: io::Error, + }, + CreatingDirectory { + path: PathBuf, + error: io::Error, + }, } pub type Result = ::std::result::Result; @@ -18,169 +27,186 @@ pub type NotifyHandler<'a> = notify::NotifyHandler<'a, for<'b> Notifyable Notifyable>>; pub enum Notification<'a> { - CreatingRoot(&'a Path), - CreatingFile(&'a Path), - CreatingDirectory(&'a Path), - FileDeletion(&'a Path, io::Result<()>), - DirectoryDeletion(&'a Path, io::Result<()>), + CreatingRoot(&'a Path), + CreatingFile(&'a Path), + CreatingDirectory(&'a Path), + FileDeletion(&'a Path, io::Result<()>), + DirectoryDeletion(&'a Path, io::Result<()>), } pub struct Cfg { - root_directory: PathBuf, - notify_handler: SharedNotifyHandler, + root_directory: PathBuf, + notify_handler: SharedNotifyHandler, } pub struct Dir<'a> { - cfg: &'a Cfg, - path: PathBuf, + cfg: &'a Cfg, + path: PathBuf, } pub struct File<'a> { - cfg: &'a Cfg, - path: PathBuf, + cfg: &'a Cfg, + path: PathBuf, } impl<'a> Notification<'a> { - pub fn level(&self) -> NotificationLevel { - use self::Notification::*; - match *self { - CreatingRoot(_) | CreatingFile(_) | CreatingDirectory(_) => - NotificationLevel::Verbose, - FileDeletion(_, ref result) | DirectoryDeletion(_, ref result) => - if result.is_ok() { - NotificationLevel::Verbose - } else { - NotificationLevel::Warn - } - } - } + pub fn level(&self) -> NotificationLevel { + use self::Notification::*; + match *self { + CreatingRoot(_) | CreatingFile(_) | CreatingDirectory(_) => NotificationLevel::Verbose, + FileDeletion(_, ref result) | DirectoryDeletion(_, ref result) => if result.is_ok() { + NotificationLevel::Verbose + } else { + NotificationLevel::Warn + }, + } + } } impl<'a> Display for Notification<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> { - use self::Notification::*; - match *self { - CreatingRoot(path) => - write!(f, "creating temp root: {}", path.display()), - CreatingFile(path) => - write!(f, "creating temp file: {}", path.display()), - CreatingDirectory(path) => - write!(f, "creating temp directory: {}", path.display()), - FileDeletion(path, ref result) => - if result.is_ok() { - write!(f, "deleted temp file: {}", path.display()) - } else { - write!(f, "could not delete temp file: {}", path.display()) - }, - DirectoryDeletion(path, ref result) => - if result.is_ok() { - write!(f, "deleted temp directory: {}", path.display()) - } else { - write!(f, "could not delete temp directory: {}", path.display()) - }, - } - } + fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> { + use self::Notification::*; + match *self { + CreatingRoot(path) => write!(f, "creating temp root: {}", path.display()), + CreatingFile(path) => write!(f, "creating temp file: {}", path.display()), + CreatingDirectory(path) => write!(f, "creating temp directory: {}", path.display()), + FileDeletion(path, ref result) => if result.is_ok() { + write!(f, "deleted temp file: {}", path.display()) + } else { + write!(f, "could not delete temp file: {}", path.display()) + }, + DirectoryDeletion(path, ref result) => if result.is_ok() { + write!(f, "deleted temp directory: {}", path.display()) + } else { + write!(f, "could not delete temp directory: {}", path.display()) + }, + } + } } impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> { - use self::Error::*; - match *self { - CreatingRoot { ref path, error: _ } => - write!(f, "could not create temp root: {}", path.display()), - CreatingFile { ref path, error: _ } => - write!(f, "could not create temp file: {}", path.display()), - CreatingDirectory { ref path, error: _ } => - write!(f, "could not create temp directory: {}", path.display()), - } - } + fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> { + use self::Error::*; + match *self { + CreatingRoot { ref path, error: _ } => + write!(f, "could not create temp root: {}", path.display()), + CreatingFile { ref path, error: _ } => + write!(f, "could not create temp file: {}", path.display()), + CreatingDirectory { ref path, error: _ } => + write!(f, "could not create temp directory: {}", path.display()), + } + } } impl Cfg { - pub fn new(root_directory: PathBuf, notify_handler: SharedNotifyHandler) -> Self { - Cfg { - root_directory: root_directory, - notify_handler: notify_handler, - } - } - - pub fn create_root(&self) -> Result { - raw::ensure_dir_exists(&self.root_directory, |p| { - self.notify_handler.call(Notification::CreatingRoot(p)); - }).map_err(|e| Error::CreatingRoot { path: PathBuf::from(&self.root_directory), error: e }) - } - - pub fn new_directory(&self) -> Result { - try!(self.create_root()); - - loop { - let temp_name = raw::random_string(16) + "_dir"; - - let temp_dir = self.root_directory.join(temp_name); - - // This is technically racey, but the probability of getting the same - // random names at exactly the same time is... low. - if !raw::path_exists(&temp_dir) { - self.notify_handler.call(Notification::CreatingDirectory(&temp_dir)); - try!(fs::create_dir(&temp_dir) - .map_err(|e| Error::CreatingDirectory { path: PathBuf::from(&temp_dir), error: e })); - return Ok(Dir { cfg: self, path: temp_dir }); - } - } - } - - pub fn new_file(&self) -> Result { - self.new_file_with_ext("", "") - } - - pub fn new_file_with_ext(&self, prefix: &str, ext: &str) -> Result { - try!(self.create_root()); - - loop { - let temp_name = prefix.to_owned() + &raw::random_string(16) + "_file" + ext; - - let temp_file = self.root_directory.join(temp_name); - - // This is technically racey, but the probability of getting the same - // random names at exactly the same time is... low. - if !raw::path_exists(&temp_file) { - self.notify_handler.call(Notification::CreatingFile(&temp_file)); - try!(fs::File::create(&temp_file) - .map_err(|e| Error::CreatingFile { path: PathBuf::from(&temp_file), error: e })); - return Ok(File { cfg: self, path: temp_file }); - } - } - } + pub fn new(root_directory: PathBuf, notify_handler: SharedNotifyHandler) -> Self { + Cfg { + root_directory: root_directory, + notify_handler: notify_handler, + } + } + + pub fn create_root(&self) -> Result { + raw::ensure_dir_exists(&self.root_directory, |p| { + self.notify_handler.call(Notification::CreatingRoot(p)); + }) + .map_err(|e| { + Error::CreatingRoot { + path: PathBuf::from(&self.root_directory), + error: e, + } + }) + } + + pub fn new_directory(&self) -> Result { + try!(self.create_root()); + + loop { + let temp_name = raw::random_string(16) + "_dir"; + + let temp_dir = self.root_directory.join(temp_name); + + // This is technically racey, but the probability of getting the same + // random names at exactly the same time is... low. + if !raw::path_exists(&temp_dir) { + self.notify_handler.call(Notification::CreatingDirectory(&temp_dir)); + try!(fs::create_dir(&temp_dir).map_err(|e| { + Error::CreatingDirectory { + path: PathBuf::from(&temp_dir), + error: e, + } + })); + return Ok(Dir { + cfg: self, + path: temp_dir, + }); + } + } + } + + pub fn new_file(&self) -> Result { + self.new_file_with_ext("", "") + } + + pub fn new_file_with_ext(&self, prefix: &str, ext: &str) -> Result { + try!(self.create_root()); + + loop { + let temp_name = prefix.to_owned() + &raw::random_string(16) + "_file" + ext; + + let temp_file = self.root_directory.join(temp_name); + + // This is technically racey, but the probability of getting the same + // random names at exactly the same time is... low. + if !raw::path_exists(&temp_file) { + self.notify_handler.call(Notification::CreatingFile(&temp_file)); + try!(fs::File::create(&temp_file).map_err(|e| { + Error::CreatingFile { + path: PathBuf::from(&temp_file), + error: e, + } + })); + return Ok(File { + cfg: self, + path: temp_file, + }); + } + } + } } impl<'a> ops::Deref for Dir<'a> { - type Target = Path; - - fn deref(&self) -> &Path { - ops::Deref::deref(&self.path) - } + type Target = Path; + + fn deref(&self) -> &Path { + ops::Deref::deref(&self.path) + } } impl<'a> ops::Deref for File<'a> { - type Target = Path; - - fn deref(&self) -> &Path { - ops::Deref::deref(&self.path) - } + type Target = Path; + + fn deref(&self) -> &Path { + ops::Deref::deref(&self.path) + } } impl<'a> Drop for Dir<'a> { - fn drop(&mut self) { - if raw::is_directory(&self.path) { - self.cfg.notify_handler.call(Notification::DirectoryDeletion(&self.path, fs::remove_dir_all(&self.path))); - } - } + fn drop(&mut self) { + if raw::is_directory(&self.path) { + self.cfg + .notify_handler + .call(Notification::DirectoryDeletion(&self.path, fs::remove_dir_all(&self.path))); + } + } } impl<'a> Drop for File<'a> { - fn drop(&mut self) { - if raw::is_file(&self.path) { - self.cfg.notify_handler.call(Notification::FileDeletion(&self.path, fs::remove_file(&self.path))); - } - } + fn drop(&mut self) { + if raw::is_file(&self.path) { + self.cfg + .notify_handler + .call(Notification::FileDeletion(&self.path, fs::remove_file(&self.path))); + } + } } diff --git a/rust-install/src/utils/mod.rs b/rust-install/src/utils/mod.rs index d705b8eb16..269db5b722 100644 --- a/rust-install/src/utils/mod.rs +++ b/rust-install/src/utils/mod.rs @@ -13,349 +13,574 @@ use notify::{self, NotificationLevel, Notifyable}; pub mod raw; -pub use self::raw::{ - is_directory, - is_file, - path_exists, - if_not_empty, - random_string, - prefix_arg, - home_dir, -}; +pub use self::raw::{is_directory, is_file, path_exists, if_not_empty, random_string, prefix_arg, + home_dir}; pub enum Notification<'a> { - CreatingDirectory(&'a str, &'a Path), - LinkingDirectory(&'a Path, &'a Path), - CopyingDirectory(&'a Path, &'a Path), - RemovingDirectory(&'a str, &'a Path), - DownloadingFile(&'a hyper::Url, &'a Path), - NoCanonicalPath(&'a Path), + CreatingDirectory(&'a str, &'a Path), + LinkingDirectory(&'a Path, &'a Path), + CopyingDirectory(&'a Path, &'a Path), + RemovingDirectory(&'a str, &'a Path), + DownloadingFile(&'a hyper::Url, &'a Path), + NoCanonicalPath(&'a Path), } pub enum Error { - LocatingHome, - LocatingWorkingDir { error: io::Error }, - ReadingFile { name: &'static str, path: PathBuf, error: io::Error }, - ReadingDirectory { name: &'static str, path: PathBuf, error: io::Error }, - WritingFile { name: &'static str, path: PathBuf, error: io::Error }, - CreatingDirectory { name: &'static str, path: PathBuf, error: io::Error }, - FilteringFile { name: &'static str, src: PathBuf, dest: PathBuf, error: io::Error }, - RenamingFile { name: &'static str, src: PathBuf, dest: PathBuf, error: io::Error }, - RenamingDirectory { name: &'static str, src: PathBuf, dest: PathBuf, error: io::Error }, - DownloadingFile { url: hyper::Url, path: PathBuf, error: raw::DownloadError }, - InvalidUrl { url: String }, - RunningCommand { name: OsString, error: raw::CommandError }, - NotAFile { path: PathBuf }, - NotADirectory { path: PathBuf }, - LinkingFile { src: PathBuf, dest: PathBuf, error: io::Error }, - LinkingDirectory { src: PathBuf, dest: PathBuf, error: io::Error }, - CopyingDirectory { src: PathBuf, dest: PathBuf, error: raw::CommandError }, - CopyingFile { src: PathBuf, dest: PathBuf, error: io::Error }, - RemovingFile { name: &'static str, path: PathBuf, error: io::Error }, - RemovingDirectory { name: &'static str, path: PathBuf, error: io::Error }, - OpeningBrowser { error: Option }, - SettingPermissions { path: PathBuf, error: io::Error }, + LocatingHome, + LocatingWorkingDir { + error: io::Error, + }, + ReadingFile { + name: &'static str, + path: PathBuf, + error: io::Error, + }, + ReadingDirectory { + name: &'static str, + path: PathBuf, + error: io::Error, + }, + WritingFile { + name: &'static str, + path: PathBuf, + error: io::Error, + }, + CreatingDirectory { + name: &'static str, + path: PathBuf, + error: io::Error, + }, + FilteringFile { + name: &'static str, + src: PathBuf, + dest: PathBuf, + error: io::Error, + }, + RenamingFile { + name: &'static str, + src: PathBuf, + dest: PathBuf, + error: io::Error, + }, + RenamingDirectory { + name: &'static str, + src: PathBuf, + dest: PathBuf, + error: io::Error, + }, + DownloadingFile { + url: hyper::Url, + path: PathBuf, + error: raw::DownloadError, + }, + InvalidUrl { + url: String, + }, + RunningCommand { + name: OsString, + error: raw::CommandError, + }, + NotAFile { + path: PathBuf, + }, + NotADirectory { + path: PathBuf, + }, + LinkingFile { + src: PathBuf, + dest: PathBuf, + error: io::Error, + }, + LinkingDirectory { + src: PathBuf, + dest: PathBuf, + error: io::Error, + }, + CopyingDirectory { + src: PathBuf, + dest: PathBuf, + error: raw::CommandError, + }, + CopyingFile { + src: PathBuf, + dest: PathBuf, + error: io::Error, + }, + RemovingFile { + name: &'static str, + path: PathBuf, + error: io::Error, + }, + RemovingDirectory { + name: &'static str, + path: PathBuf, + error: io::Error, + }, + OpeningBrowser { + error: Option, + }, + SettingPermissions { + path: PathBuf, + error: io::Error, + }, } pub type Result = ::std::result::Result; pub type NotifyHandler<'a> = notify::NotifyHandler<'a, for<'b> Notifyable>>; impl<'a> Notification<'a> { - pub fn level(&self) -> NotificationLevel { - use self::Notification::*; - match *self { - CreatingDirectory(_, _) | RemovingDirectory(_, _) => - NotificationLevel::Verbose, - LinkingDirectory(_, _) | CopyingDirectory(_, _) | DownloadingFile(_, _) => - NotificationLevel::Normal, - NoCanonicalPath(_) => - NotificationLevel::Warn, - } - } + pub fn level(&self) -> NotificationLevel { + use self::Notification::*; + match *self { + CreatingDirectory(_, _) | RemovingDirectory(_, _) => NotificationLevel::Verbose, + LinkingDirectory(_, _) | CopyingDirectory(_, _) | DownloadingFile(_, _) => + NotificationLevel::Normal, + NoCanonicalPath(_) => NotificationLevel::Warn, + } + } } impl<'a> Display for Notification<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> { - use self::Notification::*; - match *self { - CreatingDirectory(name, path) => - write!(f, "creating {} directory: '{}'", name, path.display()), - LinkingDirectory(_, dest) => - write!(f, "linking directory from: '{}'", dest.display()), - CopyingDirectory(src, _) => - write!(f, "coping directory from: '{}'", src.display()), - RemovingDirectory(name, path) => - write!(f, "removing {} directory: '{}'", name, path.display()), - DownloadingFile(url, _) => - write!(f, "downloading file from: '{}'", url), - NoCanonicalPath(path) => - write!(f, "could not canonicalize path: '{}'", path.display()), - } - } + fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> { + use self::Notification::*; + match *self { + CreatingDirectory(name, path) => + write!(f, "creating {} directory: '{}'", name, path.display()), + LinkingDirectory(_, dest) => write!(f, "linking directory from: '{}'", dest.display()), + CopyingDirectory(src, _) => write!(f, "coping directory from: '{}'", src.display()), + RemovingDirectory(name, path) => + write!(f, "removing {} directory: '{}'", name, path.display()), + DownloadingFile(url, _) => write!(f, "downloading file from: '{}'", url), + NoCanonicalPath(path) => write!(f, "could not canonicalize path: '{}'", path.display()), + } + } } impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> { - use self::Error::*; - match *self { - LocatingHome => - write!(f, "could not locate home directory"), - LocatingWorkingDir { ref error } => - write!(f, "could not locate working directory ({})", error), - ReadingFile { ref name, ref path, ref error } => - write!(f, "could not read {} file: '{}' ({})", name, path.display(), error), - ReadingDirectory { ref name, ref path, ref error } => - write!(f, "could not read {} directory: '{}' ({})", name, path.display(), error), - WritingFile { ref name, ref path, ref error } => - write!(f, "could not write {} file: '{}' ({})", name, path.display(), error), - CreatingDirectory { ref name, ref path, ref error } => - write!(f, "could not create {} directory: '{}' ({})", name, path.display(), error), - FilteringFile { ref name, ref src, ref dest, ref error } => - write!(f, "could not copy {} file from '{}' to '{}' ({})", name, src.display(), dest.display(), error ), - RenamingFile { ref name, ref src, ref dest, ref error } => - write!(f, "could not rename {} file from '{}' to '{}' ({})", name, src.display(), dest.display(), error ), - RenamingDirectory { ref name, ref src, ref dest, ref error } => - write!(f, "could not rename {} directory from '{}' to '{}' ({})", name, src.display(), dest.display(), error ), - DownloadingFile { ref url, ref path, ref error } => - write!(f, "could not download file from '{}' to '{}' ({})", url, path.display(), error), - InvalidUrl { ref url } => - write!(f, "invalid url: '{}'", url), - RunningCommand { ref name, ref error } => - write!(f, "command failed: '{}' ({})", PathBuf::from(name).display(), error), - NotAFile { ref path } => - write!(f, "not a file: '{}'", path.display()), - NotADirectory { ref path } => - write!(f, "not a directory: '{}'", path.display()), - LinkingFile { ref src, ref dest, ref error } => - write!(f, "could not create link from '{}' to '{}' ({})", src.display(), dest.display(), error), - LinkingDirectory { ref src, ref dest, ref error } => - write!(f, "could not create symlink from '{}' to '{}' ({})", src.display(), dest.display(), error), - CopyingDirectory { ref src, ref dest, ref error } => - write!(f, "could not copy directory from '{}' to '{}' ({})", src.display(), dest.display(), error), - CopyingFile { ref src, ref dest, ref error } => - write!(f, "could not copy file from '{}' to '{}' ({})", src.display(), dest.display(), error), - RemovingFile { ref name, ref path, ref error } => - write!(f, "could not remove {} file: '{}' ({})", name, path.display(), error), - RemovingDirectory { ref name, ref path, ref error } => - write!(f, "could not remove {} directory: '{} ({})'", name, path.display(), error), - OpeningBrowser { error: Some(ref e) } => - write!(f, "could not open browser: {}", e), - OpeningBrowser { error: None } => - write!(f, "could not open browser: no browser installed"), - SettingPermissions { ref path, ref error } => - write!(f, "failed to set permissions for: '{} ({})'", path.display(), error), - } - } -} - -pub fn ensure_dir_exists(name: &'static str, path: &Path, notify_handler: NotifyHandler) -> Result { - raw::ensure_dir_exists(path, |p| { - notify_handler.call(Notification::CreatingDirectory(name, p)) - }).map_err(|e| Error::CreatingDirectory { name: name, path: PathBuf::from(path), error: e }) + fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> { + use self::Error::*; + match *self { + LocatingHome => write!(f, "could not locate home directory"), + LocatingWorkingDir { ref error } => + write!(f, "could not locate working directory ({})", error), + ReadingFile { ref name, ref path, ref error } => + write!(f, + "could not read {} file: '{}' ({})", + name, + path.display(), + error), + ReadingDirectory { ref name, ref path, ref error } => + write!(f, + "could not read {} directory: '{}' ({})", + name, + path.display(), + error), + WritingFile { ref name, ref path, ref error } => + write!(f, + "could not write {} file: '{}' ({})", + name, + path.display(), + error), + CreatingDirectory { ref name, ref path, ref error } => + write!(f, + "could not create {} directory: '{}' ({})", + name, + path.display(), + error), + FilteringFile { ref name, ref src, ref dest, ref error } => + write!(f, + "could not copy {} file from '{}' to '{}' ({})", + name, + src.display(), + dest.display(), + error), + RenamingFile { ref name, ref src, ref dest, ref error } => + write!(f, + "could not rename {} file from '{}' to '{}' ({})", + name, + src.display(), + dest.display(), + error), + RenamingDirectory { ref name, ref src, ref dest, ref error } => + write!(f, + "could not rename {} directory from '{}' to '{}' ({})", + name, + src.display(), + dest.display(), + error), + DownloadingFile { ref url, ref path, ref error } => + write!(f, + "could not download file from '{}' to '{}' ({})", + url, + path.display(), + error), + InvalidUrl { ref url } => write!(f, "invalid url: '{}'", url), + RunningCommand { ref name, ref error } => write!(f, + "command failed: '{}' ({})", + PathBuf::from(name).display(), + error), + NotAFile { ref path } => write!(f, "not a file: '{}'", path.display()), + NotADirectory { ref path } => write!(f, "not a directory: '{}'", path.display()), + LinkingFile { ref src, ref dest, ref error } => + write!(f, + "could not create link from '{}' to '{}' ({})", + src.display(), + dest.display(), + error), + LinkingDirectory { ref src, ref dest, ref error } => + write!(f, + "could not create symlink from '{}' to '{}' ({})", + src.display(), + dest.display(), + error), + CopyingDirectory { ref src, ref dest, ref error } => + write!(f, + "could not copy directory from '{}' to '{}' ({})", + src.display(), + dest.display(), + error), + CopyingFile { ref src, ref dest, ref error } => + write!(f, + "could not copy file from '{}' to '{}' ({})", + src.display(), + dest.display(), + error), + RemovingFile { ref name, ref path, ref error } => + write!(f, + "could not remove {} file: '{}' ({})", + name, + path.display(), + error), + RemovingDirectory { ref name, ref path, ref error } => + write!(f, + "could not remove {} directory: '{} ({})'", + name, + path.display(), + error), + OpeningBrowser { error: Some(ref e) } => write!(f, "could not open browser: {}", e), + OpeningBrowser { error: None } => + write!(f, "could not open browser: no browser installed"), + SettingPermissions { ref path, ref error } => + write!(f, + "failed to set permissions for: '{} ({})'", + path.display(), + error), + } + } +} + +pub fn ensure_dir_exists(name: &'static str, + path: &Path, + notify_handler: NotifyHandler) + -> Result { + raw::ensure_dir_exists(path, + |p| notify_handler.call(Notification::CreatingDirectory(name, p))) + .map_err(|e| { + Error::CreatingDirectory { + name: name, + path: PathBuf::from(path), + error: e, + } + }) } pub fn read_file(name: &'static str, path: &Path) -> Result { - raw::read_file(path) - .map_err(|e| Error::ReadingFile { name: name, path: PathBuf::from(path), error: e }) + raw::read_file(path).map_err(|e| { + Error::ReadingFile { + name: name, + path: PathBuf::from(path), + error: e, + } + }) } pub fn write_file(name: &'static str, path: &Path, contents: &str) -> Result<()> { - raw::write_file(path, contents) - .map_err(|e| Error::WritingFile { name: name, path: PathBuf::from(path), error: e }) + raw::write_file(path, contents).map_err(|e| { + Error::WritingFile { + name: name, + path: PathBuf::from(path), + error: e, + } + }) } pub fn append_file(name: &'static str, path: &Path, line: &str) -> Result<()> { - raw::append_file(path, line) - .map_err(|e| Error::WritingFile { name: name, path: PathBuf::from(path), error: e }) + raw::append_file(path, line).map_err(|e| { + Error::WritingFile { + name: name, + path: PathBuf::from(path), + error: e, + } + }) } pub fn rename_file(name: &'static str, src: &Path, dest: &Path) -> Result<()> { - fs::rename(src, dest) - .map_err(|e| Error::RenamingFile { - name: name, - src: PathBuf::from(src), - dest: PathBuf::from(dest), - error: e - }) + fs::rename(src, dest).map_err(|e| { + Error::RenamingFile { + name: name, + src: PathBuf::from(src), + dest: PathBuf::from(dest), + error: e, + } + }) } pub fn rename_dir(name: &'static str, src: &Path, dest: &Path) -> Result<()> { - fs::rename(src, dest) - .map_err(|e| Error::RenamingDirectory { - name: name, - src: PathBuf::from(src), - dest: PathBuf::from(dest), - error: e - }) -} - -pub fn filter_file bool>(name: &'static str, src: &Path, dest: &Path, filter: F) -> Result { - raw::filter_file(src, dest, filter) - .map_err(|e| Error::FilteringFile { - name: name, - src: PathBuf::from(src), - dest: PathBuf::from(dest), - error: e - }) -} - -pub fn match_file Option>(name: &'static str, src: &Path, f: F) -> Result> { - raw::match_file(src, f) - .map_err(|e| Error::ReadingFile { - name: name, - path: PathBuf::from(src), - error: e - }) + fs::rename(src, dest).map_err(|e| { + Error::RenamingDirectory { + name: name, + src: PathBuf::from(src), + dest: PathBuf::from(dest), + error: e, + } + }) +} + +pub fn filter_file bool>(name: &'static str, + src: &Path, + dest: &Path, + filter: F) + -> Result { + raw::filter_file(src, dest, filter).map_err(|e| { + Error::FilteringFile { + name: name, + src: PathBuf::from(src), + dest: PathBuf::from(dest), + error: e, + } + }) +} + +pub fn match_file Option>(name: &'static str, + src: &Path, + f: F) + -> Result> { + raw::match_file(src, f).map_err(|e| { + Error::ReadingFile { + name: name, + path: PathBuf::from(src), + error: e, + } + }) } pub fn canonicalize_path(path: &Path, notify_handler: NotifyHandler) -> PathBuf { - fs::canonicalize(path) - .unwrap_or_else(|_| { - notify_handler.call(Notification::NoCanonicalPath(path)); - PathBuf::from(path) - }) + fs::canonicalize(path).unwrap_or_else(|_| { + notify_handler.call(Notification::NoCanonicalPath(path)); + PathBuf::from(path) + }) } pub fn tee_file(name: &'static str, path: &Path, w: &mut W) -> Result<()> { - raw::tee_file(path, w) - .map_err(|e| Error::ReadingFile { name: name, path: PathBuf::from(path), error: e }) -} - -pub fn download_file(url: hyper::Url, path: &Path, hasher: Option<&mut Hasher>, notify_handler: NotifyHandler) -> Result<()> { - notify_handler.call(Notification::DownloadingFile(&url, path)); - raw::download_file(url.clone(), path, hasher) - .map_err(|e| Error::DownloadingFile { url: url, path: PathBuf::from(path), error: e }) + raw::tee_file(path, w).map_err(|e| { + Error::ReadingFile { + name: name, + path: PathBuf::from(path), + error: e, + } + }) +} + +pub fn download_file(url: hyper::Url, + path: &Path, + hasher: Option<&mut Hasher>, + notify_handler: NotifyHandler) + -> Result<()> { + notify_handler.call(Notification::DownloadingFile(&url, path)); + raw::download_file(url.clone(), path, hasher).map_err(|e| { + Error::DownloadingFile { + url: url, + path: PathBuf::from(path), + error: e, + } + }) } pub fn parse_url(url: &str) -> Result { - hyper::Url::parse(url).map_err(|_| Error::InvalidUrl { url: url.to_owned() }) + hyper::Url::parse(url).map_err(|_| Error::InvalidUrl { url: url.to_owned() }) } pub fn cmd_status(name: &'static str, cmd: &mut Command) -> Result<()> { - raw::cmd_status(cmd) - .map_err(|e| Error::RunningCommand { name: OsString::from(name), error: e }) + raw::cmd_status(cmd).map_err(|e| { + Error::RunningCommand { + name: OsString::from(name), + error: e, + } + }) } pub fn assert_is_file(path: &Path) -> Result<()> { - if !is_file(path) { - Err(Error::NotAFile { path: PathBuf::from(path) }) - } else { - Ok(()) - } + if !is_file(path) { + Err(Error::NotAFile { path: PathBuf::from(path) }) + } else { + Ok(()) + } } pub fn assert_is_directory(path: &Path) -> Result<()> { - if !is_directory(path) { - Err(Error::NotADirectory { path: PathBuf::from(path) }) - } else { - Ok(()) - } + if !is_directory(path) { + Err(Error::NotADirectory { path: PathBuf::from(path) }) + } else { + Ok(()) + } } pub fn symlink_dir(src: &Path, dest: &Path, notify_handler: NotifyHandler) -> Result<()> { - notify_handler.call(Notification::LinkingDirectory(src, dest)); - raw::symlink_dir(src, dest) - .map_err(|e| Error::LinkingDirectory { src: PathBuf::from(src), dest: PathBuf::from(dest), error: e }) + notify_handler.call(Notification::LinkingDirectory(src, dest)); + raw::symlink_dir(src, dest).map_err(|e| { + Error::LinkingDirectory { + src: PathBuf::from(src), + dest: PathBuf::from(dest), + error: e, + } + }) } pub fn symlink_file(src: &Path, dest: &Path) -> Result<()> { - raw::symlink_file(src, dest) - .map_err(|e| Error::LinkingFile { src: PathBuf::from(src), dest: PathBuf::from(dest), error: e }) + raw::symlink_file(src, dest).map_err(|e| { + Error::LinkingFile { + src: PathBuf::from(src), + dest: PathBuf::from(dest), + error: e, + } + }) } pub fn hardlink_file(src: &Path, dest: &Path) -> Result<()> { - raw::hardlink(src, dest) - .map_err(|e| Error::LinkingFile { src: PathBuf::from(src), dest: PathBuf::from(dest), error: e }) + raw::hardlink(src, dest).map_err(|e| { + Error::LinkingFile { + src: PathBuf::from(src), + dest: PathBuf::from(dest), + error: e, + } + }) } pub fn copy_dir(src: &Path, dest: &Path, notify_handler: NotifyHandler) -> Result<()> { - notify_handler.call(Notification::CopyingDirectory(src, dest)); - raw::copy_dir(src, dest) - .map_err(|e| Error::CopyingDirectory { src: PathBuf::from(src), dest: PathBuf::from(dest), error: e }) + notify_handler.call(Notification::CopyingDirectory(src, dest)); + raw::copy_dir(src, dest).map_err(|e| { + Error::CopyingDirectory { + src: PathBuf::from(src), + dest: PathBuf::from(dest), + error: e, + } + }) } pub fn copy_file(src: &Path, dest: &Path) -> Result<()> { - fs::copy(src, dest) - .map_err(|e| Error::CopyingFile { src: PathBuf::from(src), dest: PathBuf::from(dest), error: e }) - .map(|_|()) + fs::copy(src, dest) + .map_err(|e| { + Error::CopyingFile { + src: PathBuf::from(src), + dest: PathBuf::from(dest), + error: e, + } + }) + .map(|_| ()) } pub fn remove_dir(name: &'static str, path: &Path, notify_handler: NotifyHandler) -> Result<()> { - notify_handler.call(Notification::RemovingDirectory(name, path)); - raw::remove_dir(path) - .map_err(|e| Error::RemovingDirectory { name: name, path: PathBuf::from(path), error: e }) + notify_handler.call(Notification::RemovingDirectory(name, path)); + raw::remove_dir(path).map_err(|e| { + Error::RemovingDirectory { + name: name, + path: PathBuf::from(path), + error: e, + } + }) } pub fn remove_file(name: &'static str, path: &Path) -> Result<()> { - fs::remove_file(path) - .map_err(|e| Error::RemovingFile { name: name, path: PathBuf::from(path), error: e }) + fs::remove_file(path).map_err(|e| { + Error::RemovingFile { + name: name, + path: PathBuf::from(path), + error: e, + } + }) } pub fn read_dir(name: &'static str, path: &Path) -> Result { - fs::read_dir(path) - .map_err(|e| Error::ReadingDirectory { name: name, path: PathBuf::from(path), error: e }) + fs::read_dir(path).map_err(|e| { + Error::ReadingDirectory { + name: name, + path: PathBuf::from(path), + error: e, + } + }) } pub fn open_browser(path: &Path) -> Result<()> { - match raw::open_browser(path) { - Ok(true) => Ok(()), - Ok(false) => Err(Error::OpeningBrowser { error: None }), - Err(e) => Err(Error::OpeningBrowser { error: Some(e) }), - } + match raw::open_browser(path) { + Ok(true) => Ok(()), + Ok(false) => Err(Error::OpeningBrowser { error: None }), + Err(e) => Err(Error::OpeningBrowser { error: Some(e) }), + } } pub fn set_permissions(path: &Path, perms: fs::Permissions) -> Result<()> { - fs::set_permissions(path, perms).map_err(|e| Error::SettingPermissions { path: PathBuf::from(path), error: e }) + fs::set_permissions(path, perms).map_err(|e| { + Error::SettingPermissions { + path: PathBuf::from(path), + error: e, + } + }) } pub fn make_executable(path: &Path) -> Result<()> { - #[cfg(windows)] - fn inner(_: &Path) -> Result<()> { - Ok(()) - } - #[cfg(not(windows))] - fn inner(path: &Path) -> Result<()> { - use std::os::unix::fs::PermissionsExt; - - let metadata = try!(fs::metadata(path).map_err(|e| Error::SettingPermissions { path: PathBuf::from(path), error: e })); - let mut perms = metadata.permissions(); - let new_mode = perms.mode()|0o111; - perms.set_mode(new_mode); - - set_permissions(path, perms) - } - - inner(path) + #[cfg(windows)] + fn inner(_: &Path) -> Result<()> { + Ok(()) + } + #[cfg(not(windows))] + fn inner(path: &Path) -> Result<()> { + use std::os::unix::fs::PermissionsExt; + + let metadata = try!(fs::metadata(path).map_err(|e| { + Error::SettingPermissions { + path: PathBuf::from(path), + error: e, + } + })); + let mut perms = metadata.permissions(); + let new_mode = perms.mode() | 0o111; + perms.set_mode(new_mode); + + set_permissions(path, perms) + } + + inner(path) } pub fn current_dir() -> Result { - env::current_dir().map_err(|e| Error::LocatingWorkingDir { error: e }) + env::current_dir().map_err(|e| Error::LocatingWorkingDir { error: e }) } pub fn current_exe() -> Result { - env::current_exe().map_err(|e| Error::LocatingWorkingDir { error: e }) + env::current_exe().map_err(|e| Error::LocatingWorkingDir { error: e }) } pub fn to_absolute>(path: P) -> Result { - current_dir().map(|mut v| { - v.push(path); - v - }) + current_dir().map(|mut v| { + v.push(path); + v + }) } pub fn get_local_data_path() -> Result { - #[cfg(windows)] - fn inner() -> Result { - raw::windows::get_special_folder(&raw::windows::FOLDERID_LocalAppData).map_err(|_| Error::LocatingHome) - } - #[cfg(not(windows))] - fn inner() -> Result { - // TODO: consider using ~/.local/ instead - home_dir() - .ok_or(Error::LocatingHome) - .map(PathBuf::from) - .and_then(to_absolute) - } - - inner() + #[cfg(windows)] + fn inner() -> Result { + raw::windows::get_special_folder(&raw::windows::FOLDERID_LocalAppData) + .map_err(|_| Error::LocatingHome) + } + #[cfg(not(windows))] + fn inner() -> Result { + // TODO: consider using ~/.local/ instead + home_dir() + .ok_or(Error::LocatingHome) + .map(PathBuf::from) + .and_then(to_absolute) + } + + inner() } diff --git a/rust-install/src/utils/raw.rs b/rust-install/src/utils/raw.rs index cbd94f8e64..0d677da951 100644 --- a/rust-install/src/utils/raw.rs +++ b/rust-install/src/utils/raw.rs @@ -12,384 +12,404 @@ use openssl::crypto::hash::Hasher; use rand::random; -pub fn ensure_dir_exists, F: FnOnce(&Path)>(path: P, callback: F) -> io::Result { - if !is_directory(path.as_ref()) { - callback(path.as_ref()); - fs::create_dir_all(path.as_ref()).map(|()| true) - } else { - Ok(false) - } +pub fn ensure_dir_exists, F: FnOnce(&Path)>(path: P, + callback: F) + -> io::Result { + if !is_directory(path.as_ref()) { + callback(path.as_ref()); + fs::create_dir_all(path.as_ref()).map(|()| true) + } else { + Ok(false) + } } pub fn is_directory>(path: P) -> bool { - fs::metadata(path).ok().as_ref().map(fs::Metadata::is_dir) == Some(true) + fs::metadata(path).ok().as_ref().map(fs::Metadata::is_dir) == Some(true) } pub fn is_file>(path: P) -> bool { - fs::metadata(path).ok().as_ref().map(fs::Metadata::is_file) == Some(true) + fs::metadata(path).ok().as_ref().map(fs::Metadata::is_file) == Some(true) } pub fn path_exists>(path: P) -> bool { - fs::metadata(path).is_ok() + fs::metadata(path).is_ok() } pub fn random_string(length: usize) -> String { - let chars = b"abcdefghijklmnopqrstuvwxyz0123456789_"; - (0..length).map(|_| from_u32(chars[random::() % chars.len()] as u32).unwrap()).collect() + let chars = b"abcdefghijklmnopqrstuvwxyz0123456789_"; + (0..length).map(|_| from_u32(chars[random::() % chars.len()] as u32).unwrap()).collect() } pub fn if_not_empty>(s: S) -> Option { - if s == *"" { - None - } else { - Some(s) - } + if s == *"" { + None + } else { + Some(s) + } } pub fn write_file(path: &Path, contents: &str) -> io::Result<()> { - let mut file = try!(fs::OpenOptions::new() - .write(true) - .truncate(true) - .create(true) - .open(path)); - - try!(io::Write::write_all(&mut file, contents.as_bytes())); - - try!(file.sync_data()); - - Ok(()) + let mut file = try!(fs::OpenOptions::new() + .write(true) + .truncate(true) + .create(true) + .open(path)); + + try!(io::Write::write_all(&mut file, contents.as_bytes())); + + try!(file.sync_data()); + + Ok(()) } pub fn read_file(path: &Path) -> io::Result { - let mut file = try!(fs::OpenOptions::new() - .read(true) - .open(path)); - - let mut contents = String::new(); - - try!(io::Read::read_to_string(&mut file, &mut contents)); - - Ok(contents) + let mut file = try!(fs::OpenOptions::new() + .read(true) + .open(path)); + + let mut contents = String::new(); + + try!(io::Read::read_to_string(&mut file, &mut contents)); + + Ok(contents) } -pub fn filter_file bool>(src: &Path, dest: &Path, mut filter: F) -> io::Result { - let src_file = try!(fs::File::open(src)); - let dest_file = try!(fs::File::create(dest)); - - let mut reader = io::BufReader::new(src_file); - let mut writer = io::BufWriter::new(dest_file); - let mut removed = 0; - - for result in io::BufRead::lines(&mut reader) { - let line = try!(result); - if filter(&line) { - try!(writeln!(&mut writer, "{}", &line)); - } else { - removed += 1; - } - } - - try!(writer.flush()); - - Ok(removed) +pub fn filter_file bool>(src: &Path, + dest: &Path, + mut filter: F) + -> io::Result { + let src_file = try!(fs::File::open(src)); + let dest_file = try!(fs::File::create(dest)); + + let mut reader = io::BufReader::new(src_file); + let mut writer = io::BufWriter::new(dest_file); + let mut removed = 0; + + for result in io::BufRead::lines(&mut reader) { + let line = try!(result); + if filter(&line) { + try!(writeln!(&mut writer, "{}", &line)); + } else { + removed += 1; + } + } + + try!(writer.flush()); + + Ok(removed) } pub fn match_file Option>(src: &Path, mut f: F) -> io::Result> { - let src_file = try!(fs::File::open(src)); - - let mut reader = io::BufReader::new(src_file); - - for result in io::BufRead::lines(&mut reader) { - let line = try!(result); - if let Some(r) = f(&line) { - return Ok(Some(r)); - } - } - - Ok(None) + let src_file = try!(fs::File::open(src)); + + let mut reader = io::BufReader::new(src_file); + + for result in io::BufRead::lines(&mut reader) { + let line = try!(result); + if let Some(r) = f(&line) { + return Ok(Some(r)); + } + } + + Ok(None) } pub fn append_file(dest: &Path, line: &str) -> io::Result<()> { - let mut dest_file = try!(fs::OpenOptions::new() - .write(true) - .append(true) - .create(true) - .open(dest) - ); - - try!(writeln!(&mut dest_file, "{}", line)); - - try!(dest_file.sync_data()); - - Ok(()) + let mut dest_file = try!(fs::OpenOptions::new() + .write(true) + .append(true) + .create(true) + .open(dest)); + + try!(writeln!(&mut dest_file, "{}", line)); + + try!(dest_file.sync_data()); + + Ok(()) } pub fn tee_file(path: &Path, mut w: &mut W) -> io::Result<()> { - let mut file = try!(fs::OpenOptions::new() - .read(true) - .open(path)); - - let buffer_size = 0x10000; - let mut buffer = vec![0u8; buffer_size]; - - loop { - let bytes_read = try!(io::Read::read(&mut file, &mut buffer)); - - if bytes_read != 0 { - try!(io::Write::write_all(w, &mut buffer[0..bytes_read])); - } else { - return Ok(()); - } - } + let mut file = try!(fs::OpenOptions::new() + .read(true) + .open(path)); + + let buffer_size = 0x10000; + let mut buffer = vec![0u8; buffer_size]; + + loop { + let bytes_read = try!(io::Read::read(&mut file, &mut buffer)); + + if bytes_read != 0 { + try!(io::Write::write_all(w, &mut buffer[0..bytes_read])); + } else { + return Ok(()); + } + } } #[derive(Debug)] pub enum DownloadError { - Status(hyper::status::StatusCode), - Network(hyper::Error), - File(io::Error), + Status(hyper::status::StatusCode), + Network(hyper::Error), + File(io::Error), } pub type DownloadResult = Result; impl fmt::Display for DownloadError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - DownloadError::Status(ref s) => write!(f, "Status: {}", s), - DownloadError::Network(ref e) => write!(f, "Network: {}", e), - DownloadError::File(ref e) => write!(f, "File: {}", e), - } - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + DownloadError::Status(ref s) => write!(f, "Status: {}", s), + DownloadError::Network(ref e) => write!(f, "Network: {}", e), + DownloadError::File(ref e) => write!(f, "File: {}", e), + } + } } -pub fn download_file>(url: hyper::Url, path: P, mut hasher: Option<&mut Hasher>) -> DownloadResult<()> { - let client = Client::new(); - - let mut res = try!(client.get(url).send().map_err(DownloadError::Network)); - if res.status != hyper::Ok { return Err(DownloadError::Status(res.status)); } - - let buffer_size = 0x10000; - let mut buffer = vec![0u8; buffer_size]; - - let mut file = try!(fs::File::create(path).map_err(DownloadError::File)); - - loop { - let bytes_read = try!(io::Read::read(&mut res, &mut buffer) - .map_err(hyper::Error::Io) - .map_err(DownloadError::Network) - ); - - if bytes_read != 0 { - if let Some(ref mut h) = hasher { - try!(io::Write::write_all(*h, &mut buffer[0..bytes_read]).map_err(DownloadError::File)); - } - try!(io::Write::write_all(&mut file, &mut buffer[0..bytes_read]).map_err(DownloadError::File)); - } else { - try!(file.sync_data().map_err(DownloadError::File)); - return Ok(()); - } - } +pub fn download_file>(url: hyper::Url, + path: P, + mut hasher: Option<&mut Hasher>) + -> DownloadResult<()> { + let client = Client::new(); + + let mut res = try!(client.get(url).send().map_err(DownloadError::Network)); + if res.status != hyper::Ok { + return Err(DownloadError::Status(res.status)); + } + + let buffer_size = 0x10000; + let mut buffer = vec![0u8; buffer_size]; + + let mut file = try!(fs::File::create(path).map_err(DownloadError::File)); + + loop { + let bytes_read = try!(io::Read::read(&mut res, &mut buffer) + .map_err(hyper::Error::Io) + .map_err(DownloadError::Network)); + + if bytes_read != 0 { + if let Some(ref mut h) = hasher { + try!(io::Write::write_all(*h, &mut buffer[0..bytes_read]) + .map_err(DownloadError::File)); + } + try!(io::Write::write_all(&mut file, &mut buffer[0..bytes_read]) + .map_err(DownloadError::File)); + } else { + try!(file.sync_data().map_err(DownloadError::File)); + return Ok(()); + } + } } pub fn symlink_dir(src: &Path, dest: &Path) -> io::Result<()> { - #[cfg(windows)] - fn symlink_dir_inner(src: &Path, dest: &Path) -> io::Result<()> { - ::std::os::windows::fs::symlink_dir(src, dest) - } - #[cfg(not(windows))] - fn symlink_dir_inner(src: &Path, dest: &Path) -> io::Result<()> { - ::std::os::unix::fs::symlink(src, dest) - } - - let _ = remove_dir(dest); - symlink_dir_inner(src, dest) + #[cfg(windows)] + fn symlink_dir_inner(src: &Path, dest: &Path) -> io::Result<()> { + ::std::os::windows::fs::symlink_dir(src, dest) + } + #[cfg(not(windows))] + fn symlink_dir_inner(src: &Path, dest: &Path) -> io::Result<()> { + ::std::os::unix::fs::symlink(src, dest) + } + + let _ = remove_dir(dest); + symlink_dir_inner(src, dest) } pub fn symlink_file(src: &Path, dest: &Path) -> io::Result<()> { - #[cfg(windows)] - fn symlink_file_inner(src: &Path, dest: &Path) -> io::Result<()> { - ::std::os::windows::fs::symlink_file(src, dest) - } - #[cfg(not(windows))] - fn symlink_file_inner(src: &Path, dest: &Path) -> io::Result<()> { - ::std::os::unix::fs::symlink(src, dest) - } - - let _ = fs::remove_file(dest); - symlink_file_inner(src, dest) + #[cfg(windows)] + fn symlink_file_inner(src: &Path, dest: &Path) -> io::Result<()> { + ::std::os::windows::fs::symlink_file(src, dest) + } + #[cfg(not(windows))] + fn symlink_file_inner(src: &Path, dest: &Path) -> io::Result<()> { + ::std::os::unix::fs::symlink(src, dest) + } + + let _ = fs::remove_file(dest); + symlink_file_inner(src, dest) } pub fn hardlink(src: &Path, dest: &Path) -> io::Result<()> { - let _ = fs::remove_file(dest); - fs::hard_link(src, dest) + let _ = fs::remove_file(dest); + fs::hard_link(src, dest) } #[derive(Debug)] pub enum CommandError { - Io(io::Error), - Status(ExitStatus), + Io(io::Error), + Status(ExitStatus), } pub type CommandResult = Result; impl fmt::Display for CommandError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - CommandError::Io(ref e) => write!(f, "Io: {}", e), - CommandError::Status(ref s) => write!(f, "Status: {}", s), - } - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + CommandError::Io(ref e) => write!(f, "Io: {}", e), + CommandError::Status(ref s) => write!(f, "Status: {}", s), + } + } } pub fn cmd_status(cmd: &mut Command) -> CommandResult<()> { - cmd.status().map_err(CommandError::Io).and_then(|s| { - if s.success() { - Ok(()) - } else { - Err(CommandError::Status(s)) - } - }) + cmd.status().map_err(CommandError::Io).and_then(|s| { + if s.success() { + Ok(()) + } else { + Err(CommandError::Status(s)) + } + }) } pub fn remove_dir(path: &Path) -> io::Result<()> { - if try!(fs::symlink_metadata(path)).file_type().is_symlink() { - if cfg!(windows) { - fs::remove_dir(path) - } else { - fs::remove_file(path) - } - } else { - fs::remove_dir_all(path) - } + if try!(fs::symlink_metadata(path)).file_type().is_symlink() { + if cfg!(windows) { + fs::remove_dir(path) + } else { + fs::remove_file(path) + } + } else { + fs::remove_dir_all(path) + } } pub fn copy_dir(src: &Path, dest: &Path) -> CommandResult<()> { - #[cfg(windows)] - fn copy_dir_inner(src: &Path, dest: &Path) -> CommandResult<()> { - Command::new("robocopy") - .arg(src).arg(dest).arg("/E") - .arg("/NFL").arg("/NDL").arg("/NJH").arg("/NJS").arg("/nc").arg("/ns").arg("/np") - .status().map_err(CommandError::Io) - .and_then(|s| { - match s.code() { - // Robocopy has non-zero exit codes for successful copies... - Some(value) if value < 8 => Ok(()), - _ => Err(CommandError::Status(s)), - } - }) - } - #[cfg(not(windows))] - fn copy_dir_inner(src: &Path, dest: &Path) -> CommandResult<()> { - cmd_status(Command::new("cp").arg("-R").arg(src).arg(dest)) - } - - let _ = remove_dir(dest); - copy_dir_inner(src, dest) + #[cfg(windows)] + fn copy_dir_inner(src: &Path, dest: &Path) -> CommandResult<()> { + Command::new("robocopy") + .arg(src) + .arg(dest) + .arg("/E") + .arg("/NFL") + .arg("/NDL") + .arg("/NJH") + .arg("/NJS") + .arg("/nc") + .arg("/ns") + .arg("/np") + .status() + .map_err(CommandError::Io) + .and_then(|s| { + match s.code() { + // Robocopy has non-zero exit codes for successful copies... + Some(value) if value < 8 => Ok(()), + _ => Err(CommandError::Status(s)), + } + }) + } + #[cfg(not(windows))] + fn copy_dir_inner(src: &Path, dest: &Path) -> CommandResult<()> { + cmd_status(Command::new("cp").arg("-R").arg(src).arg(dest)) + } + + let _ = remove_dir(dest); + copy_dir_inner(src, dest) } pub fn prefix_arg>(name: &str, s: S) -> OsString { - let mut arg = OsString::from(name); - arg.push(s); - arg + let mut arg = OsString::from(name); + arg.push(s); + arg } pub fn open_browser(path: &Path) -> io::Result { - #[cfg(not(windows))] - fn has_cmd(cmd: &&str) -> bool { - cmd_status(Command::new("which") - .arg(cmd) - .stdin(Stdio::null()) - .stdout(Stdio::null()) - .stderr(Stdio::null())).is_ok() - } - #[cfg(not(windows))] - fn inner(path: &Path) -> io::Result { - let commands = ["xdg-open", "open", "firefox", "chromium"]; - if let Some(cmd) = commands.iter().map(|s| *s).filter(has_cmd).next() { - Command::new(cmd) - .arg(path) - .stdin(Stdio::null()) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .spawn() - .map(|_| true) - } else { - Ok(false) - } - } - #[cfg(windows)] - fn inner(path: &Path) -> io::Result { - Command::new("cmd") - .arg("/C") - .arg("start") - .arg(path) - .stdin(Stdio::null()) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .spawn() - .map(|_| true) - } - inner(path) + #[cfg(not(windows))] + fn has_cmd(cmd: &&str) -> bool { + cmd_status(Command::new("which") + .arg(cmd) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null())) + .is_ok() + } + #[cfg(not(windows))] + fn inner(path: &Path) -> io::Result { + let commands = ["xdg-open", "open", "firefox", "chromium"]; + if let Some(cmd) = commands.iter().map(|s| *s).filter(has_cmd).next() { + Command::new(cmd) + .arg(path) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn() + .map(|_| true) + } else { + Ok(false) + } + } + #[cfg(windows)] + fn inner(path: &Path) -> io::Result { + Command::new("cmd") + .arg("/C") + .arg("start") + .arg(path) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn() + .map(|_| true) + } + inner(path) } pub fn home_dir() -> Option { - #[cfg(not(windows))] - fn inner() -> Option { - ::std::env::home_dir() - } - #[cfg(windows)] - fn inner() -> Option { - windows::get_special_folder(&windows::FOLDERID_Profile).ok() - } - - inner() + #[cfg(not(windows))] + fn inner() -> Option { + ::std::env::home_dir() + } + #[cfg(windows)] + fn inner() -> Option { + windows::get_special_folder(&windows::FOLDERID_Profile).ok() + } + + inner() } #[cfg(windows)] pub mod windows { - use winapi::*; - use std::io; - use std::path::PathBuf; - use std::ptr; - use std::slice; - use std::ffi::OsString; - use std::os::windows::ffi::OsStringExt; - use shell32; - use ole32; - - #[allow(non_upper_case_globals)] - pub const FOLDERID_LocalAppData: GUID = GUID { - Data1: 0xF1B32785, - Data2: 0x6FBA, - Data3: 0x4FCF, - Data4: [0x9D, 0x55, 0x7B, 0x8E, 0x7F, 0x15, 0x70, 0x91], - }; - #[allow(non_upper_case_globals)] - pub const FOLDERID_Profile: GUID = GUID { - Data1: 0x5E6C858F, - Data2: 0x0E22, - Data3: 0x4760, - Data4: [0x9A, 0xFE, 0xEA, 0x33, 0x17, 0xB6, 0x71, 0x73], - }; - - pub fn get_special_folder(id: &shtypes::KNOWNFOLDERID) -> io::Result { - - - let mut path = ptr::null_mut(); - let result; - - unsafe { - let code = shell32::SHGetKnownFolderPath(id, 0, ptr::null_mut(), &mut path); - if code == 0 { - let mut length = 0usize; - while *path.offset(length as isize) != 0 { - length += 1; - } - let slice = slice::from_raw_parts(path, length); - result = Ok(OsString::from_wide(slice).into()); - } else { - result = Err(io::Error::from_raw_os_error(code)); - } - ole32::CoTaskMemFree(path as *mut _); - } - result - } + use winapi::*; + use std::io; + use std::path::PathBuf; + use std::ptr; + use std::slice; + use std::ffi::OsString; + use std::os::windows::ffi::OsStringExt; + use shell32; + use ole32; + + #[allow(non_upper_case_globals)] + pub const FOLDERID_LocalAppData: GUID = GUID { + Data1: 0xF1B32785, + Data2: 0x6FBA, + Data3: 0x4FCF, + Data4: [0x9D, 0x55, 0x7B, 0x8E, 0x7F, 0x15, 0x70, 0x91], + }; + #[allow(non_upper_case_globals)] + pub const FOLDERID_Profile: GUID = GUID { + Data1: 0x5E6C858F, + Data2: 0x0E22, + Data3: 0x4760, + Data4: [0x9A, 0xFE, 0xEA, 0x33, 0x17, 0xB6, 0x71, 0x73], + }; + + pub fn get_special_folder(id: &shtypes::KNOWNFOLDERID) -> io::Result { + + + let mut path = ptr::null_mut(); + let result; + + unsafe { + let code = shell32::SHGetKnownFolderPath(id, 0, ptr::null_mut(), &mut path); + if code == 0 { + let mut length = 0usize; + while *path.offset(length as isize) != 0 { + length += 1; + } + let slice = slice::from_raw_parts(path, length); + result = Ok(OsString::from_wide(slice).into()); + } else { + result = Err(io::Error::from_raw_os_error(code)); + } + ole32::CoTaskMemFree(path as *mut _); + } + result + } } diff --git a/src/config.rs b/src/config.rs index a94d0315b7..bc417b0392 100644 --- a/src/config.rs +++ b/src/config.rs @@ -16,280 +16,292 @@ use toolchain::Toolchain; pub const METADATA_VERSION: &'static str = "2"; pub enum OverrideReason { - Environment, - OverrideDB(PathBuf), + Environment, + OverrideDB(PathBuf), } impl Display for OverrideReason { - fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> { - match *self { - OverrideReason::Environment => - write!(f, "environment override by MULTIRUST_TOOLCHAIN"), - OverrideReason::OverrideDB(ref path) => - write!(f, "directory override due to '{}'", path.display()), - } - } + fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> { + match *self { + OverrideReason::Environment => write!(f, "environment override by MULTIRUST_TOOLCHAIN"), + OverrideReason::OverrideDB(ref path) => + write!(f, "directory override due to '{}'", path.display()), + } + } } pub struct Cfg { - pub multirust_dir: PathBuf, - pub version_file: PathBuf, - pub override_db: OverrideDB, - pub default_file: PathBuf, - pub toolchains_dir: PathBuf, - pub update_hash_dir: PathBuf, - pub temp_cfg: temp::Cfg, - pub gpg_key: Cow<'static, str>, - pub env_override: Option, - pub dist_root_url: Cow<'static, str>, - pub notify_handler: SharedNotifyHandler, + pub multirust_dir: PathBuf, + pub version_file: PathBuf, + pub override_db: OverrideDB, + pub default_file: PathBuf, + pub toolchains_dir: PathBuf, + pub update_hash_dir: PathBuf, + pub temp_cfg: temp::Cfg, + pub gpg_key: Cow<'static, str>, + pub env_override: Option, + pub dist_root_url: Cow<'static, str>, + pub notify_handler: SharedNotifyHandler, } impl Cfg { - pub fn from_env(notify_handler: SharedNotifyHandler) -> Result { - // Get absolute home directory - let data_dir = try!(utils::get_local_data_path()); - - // Set up the multirust home directory - let multirust_dir = env::var_os("MULTIRUST_HOME") - .and_then(utils::if_not_empty) - .map(PathBuf::from) - .unwrap_or_else(|| data_dir.join(".multirust")); - - try!(utils::ensure_dir_exists("home", &multirust_dir, ntfy!(¬ify_handler))); - - // 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 toolchains_dir = multirust_dir.join("toolchains"); - let update_hash_dir = multirust_dir.join("update-hashes"); - - let notify_clone = notify_handler.clone(); - let temp_cfg = temp::Cfg::new( - multirust_dir.join("tmp"), - shared_ntfy!(move |n: temp::Notification| { - notify_clone.call(Notification::Temp(n)); - }), - ); - - // GPG key - let gpg_key = if let Some(path) = env::var_os("MULTIRUST_GPG_KEY").and_then(utils::if_not_empty) { - Cow::Owned(try!(utils::read_file("public key", Path::new(&path)))) - } else { - Cow::Borrowed(include_str!("rust-key.gpg.ascii")) - }; - - // Environment override - let env_override = env::var("MULTIRUST_TOOLCHAIN") - .ok().and_then(utils::if_not_empty); - - let dist_root_url = env::var("MULTIRUST_DIST_ROOT") - .ok().and_then(utils::if_not_empty) - .map(Cow::Owned) - .unwrap_or(Cow::Borrowed(dist::DEFAULT_DIST_ROOT)); - - Ok(Cfg { - multirust_dir: multirust_dir, - version_file: version_file, - override_db: override_db, - default_file: default_file, - toolchains_dir: toolchains_dir, - update_hash_dir: update_hash_dir, - temp_cfg: temp_cfg, - gpg_key: gpg_key, - notify_handler: notify_handler, - env_override: env_override, - dist_root_url: dist_root_url, - }) - } - - 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)); - - self.notify_handler.call(Notification::SetDefaultToolchain(toolchain)); - - Ok(()) - } - - pub fn get_toolchain(&self, name: &str, create_parent: bool) -> Result { - if create_parent { - try!(utils::ensure_dir_exists("toolchains", &self.toolchains_dir, ntfy!(&self.notify_handler))); - } - - Ok(Toolchain::from(self, name)) - } - - pub fn verify_toolchain(&self, name: &str) -> Result { - let toolchain = try!(self.get_toolchain(name, false)); - try!(toolchain.verify()); - Ok(toolchain) - } - - pub fn get_hash_file(&self, toolchain: &str, create_parent: bool) -> Result { - if create_parent { - try!(utils::ensure_dir_exists("update-hash", &self.update_hash_dir, ntfy!(&self.notify_handler))); - } - - Ok(self.update_hash_dir.join(toolchain)) - } - - pub fn which_binary(&self, path: &Path, binary: &str) -> Result> { - - if let Some((toolchain, _)) = try!(self.find_override_toolchain_or_default(path)) { - Ok(Some(toolchain.prefix().binary_file(binary))) - } else { - Ok(None) - } - } - - pub fn upgrade_data(&self) -> Result { - if !utils::is_file(&self.version_file) { - return Ok(false); - } - - let current_version = try!(utils::read_file("version", &self.version_file)); - - self.notify_handler.call(Notification::UpgradingMetadata(¤t_version, METADATA_VERSION)); - - match &*current_version { - "2" => { - // Current version. Do nothing - Ok(false) - }, - "1" => { - // Ignore errors. These files may not exist. - let _ = fs::remove_dir_all(self.multirust_dir.join("available-updates")); - let _ = fs::remove_dir_all(self.multirust_dir.join("update-sums")); - let _ = fs::remove_dir_all(self.multirust_dir.join("channel-sums")); - let _ = fs::remove_dir_all(self.multirust_dir.join("manifests")); - - try!(utils::write_file("version", &self.version_file, METADATA_VERSION)); - - Ok(true) - } - _ => { - Err(Error::UnknownMetadataVersion(current_version)) - } - } - } - - pub fn delete_data(&self) -> Result<()> { - if utils::path_exists(&self.multirust_dir) { - Ok(try!(utils::remove_dir("home", &self.multirust_dir, ntfy!(&self.notify_handler)))) - } else { - Ok(()) - } - } - - 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 toolchain = try!(self.verify_toolchain(name)); - - Ok(Some(toolchain)) - } - - pub fn find_override(&self, path: &Path) -> Result> { - if let Some(ref name) = self.env_override { - let toolchain = try!(self.verify_toolchain(name)); - - return Ok(Some((toolchain, OverrideReason::Environment))); - } - - if let Some((name, reason_path)) = try!(self.override_db.find(path, self.notify_handler.as_ref())) { - let toolchain = try!(self.verify_toolchain(&name)); - return Ok(Some((toolchain, OverrideReason::OverrideDB(reason_path)))); - } - - Ok(None) - } - - pub fn find_override_toolchain_or_default(&self, path: &Path) -> Result)>> { - Ok(if let Some((toolchain, reason)) = try!(self.find_override(path)) { - Some((toolchain, Some(reason))) - } else { - try!(self.find_default()).map(|toolchain| (toolchain, None)) - }) - } - - pub fn list_toolchains(&self) -> Result> { - if utils::is_directory(&self.toolchains_dir) { - let toolchains: Vec<_> = try!(utils::read_dir("toolchains", &self.toolchains_dir)) - .filter_map(io::Result::ok) - .filter_map(|e| e.file_name().into_string().ok()) - .collect(); - - Ok(toolchains) - } else { - Ok(Vec::new()) - } - } - - pub fn update_all_channels(&self) -> Result)>> { - let mut toolchains = try!(self.list_toolchains()); - toolchains.sort(); - - Ok(toolchains.into_iter() - .merge(["beta", "nightly", "stable"].into_iter().map(|s| (*s).to_owned())) - .dedup() - .filter(|name| dist::ToolchainDesc::from_str(&name).map(|d| d.is_tracking()) == Some(true)) - .map(|name| { - let result = self.get_toolchain(&name, true).and_then(|t| t.install_from_dist()); - if let Err(ref e) = result { - self.notify_handler.call(Notification::NonFatalError(e)); - } - (name, result) - }) - .collect()) - } - - 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(true) - } else { - let current_version = try!(utils::read_file("metadata version", &self.version_file)); - - self.notify_handler.call(Notification::ReadMetadataVersion(¤t_version)); - - Ok(&*current_version == METADATA_VERSION) - } - } - - pub fn toolchain_for_dir(&self, path: &Path) -> Result<(Toolchain, Option)> { - self.find_override_toolchain_or_default(path) - .and_then(|r| r.ok_or(Error::NoDefaultToolchain)) - } - - pub fn create_command_for_dir(&self, path: &Path, binary: &str) -> Result { - let (toolchain, _) = try!(self.toolchain_for_dir(path)); - toolchain.create_command(binary) - } - - pub fn doc_path_for_dir(&self, path: &Path, relative: &str) -> Result { - let (toolchain, _) = try!(self.toolchain_for_dir(path)); - toolchain.doc_path(relative) - } - - pub fn open_docs_for_dir(&self, path: &Path, relative: &str) -> Result<()> { - let (toolchain, _) = try!(self.toolchain_for_dir(path)); - toolchain.open_docs(relative) - } + pub fn from_env(notify_handler: SharedNotifyHandler) -> Result { + // Get absolute home directory + let data_dir = try!(utils::get_local_data_path()); + + // Set up the multirust home directory + let multirust_dir = env::var_os("MULTIRUST_HOME") + .and_then(utils::if_not_empty) + .map(PathBuf::from) + .unwrap_or_else(|| data_dir.join(".multirust")); + + try!(utils::ensure_dir_exists("home", &multirust_dir, ntfy!(¬ify_handler))); + + // 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 toolchains_dir = multirust_dir.join("toolchains"); + let update_hash_dir = multirust_dir.join("update-hashes"); + + let notify_clone = notify_handler.clone(); + let temp_cfg = temp::Cfg::new(multirust_dir.join("tmp"), + shared_ntfy!(move |n: temp::Notification| { + notify_clone.call(Notification::Temp(n)); + })); + + // GPG key + let gpg_key = if let Some(path) = env::var_os("MULTIRUST_GPG_KEY") + .and_then(utils::if_not_empty) { + Cow::Owned(try!(utils::read_file("public key", Path::new(&path)))) + } else { + Cow::Borrowed(include_str!("rust-key.gpg.ascii")) + }; + + // Environment override + let env_override = env::var("MULTIRUST_TOOLCHAIN") + .ok() + .and_then(utils::if_not_empty); + + let dist_root_url = env::var("MULTIRUST_DIST_ROOT") + .ok() + .and_then(utils::if_not_empty) + .map(Cow::Owned) + .unwrap_or(Cow::Borrowed(dist::DEFAULT_DIST_ROOT)); + + Ok(Cfg { + multirust_dir: multirust_dir, + version_file: version_file, + override_db: override_db, + default_file: default_file, + toolchains_dir: toolchains_dir, + update_hash_dir: update_hash_dir, + temp_cfg: temp_cfg, + gpg_key: gpg_key, + notify_handler: notify_handler, + env_override: env_override, + dist_root_url: dist_root_url, + }) + } + + 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)); + + self.notify_handler.call(Notification::SetDefaultToolchain(toolchain)); + + Ok(()) + } + + pub fn get_toolchain(&self, name: &str, create_parent: bool) -> Result { + if create_parent { + try!(utils::ensure_dir_exists("toolchains", + &self.toolchains_dir, + ntfy!(&self.notify_handler))); + } + + Ok(Toolchain::from(self, name)) + } + + pub fn verify_toolchain(&self, name: &str) -> Result { + let toolchain = try!(self.get_toolchain(name, false)); + try!(toolchain.verify()); + Ok(toolchain) + } + + pub fn get_hash_file(&self, toolchain: &str, create_parent: bool) -> Result { + if create_parent { + try!(utils::ensure_dir_exists("update-hash", + &self.update_hash_dir, + ntfy!(&self.notify_handler))); + } + + Ok(self.update_hash_dir.join(toolchain)) + } + + pub fn which_binary(&self, path: &Path, binary: &str) -> Result> { + + if let Some((toolchain, _)) = try!(self.find_override_toolchain_or_default(path)) { + Ok(Some(toolchain.prefix().binary_file(binary))) + } else { + Ok(None) + } + } + + pub fn upgrade_data(&self) -> Result { + if !utils::is_file(&self.version_file) { + return Ok(false); + } + + let current_version = try!(utils::read_file("version", &self.version_file)); + + self.notify_handler + .call(Notification::UpgradingMetadata(¤t_version, METADATA_VERSION)); + + match &*current_version { + "2" => { + // Current version. Do nothing + Ok(false) + } + "1" => { + // Ignore errors. These files may not exist. + let _ = fs::remove_dir_all(self.multirust_dir.join("available-updates")); + let _ = fs::remove_dir_all(self.multirust_dir.join("update-sums")); + let _ = fs::remove_dir_all(self.multirust_dir.join("channel-sums")); + let _ = fs::remove_dir_all(self.multirust_dir.join("manifests")); + + try!(utils::write_file("version", &self.version_file, METADATA_VERSION)); + + Ok(true) + } + _ => { + Err(Error::UnknownMetadataVersion(current_version)) + } + } + } + + pub fn delete_data(&self) -> Result<()> { + if utils::path_exists(&self.multirust_dir) { + Ok(try!(utils::remove_dir("home", &self.multirust_dir, ntfy!(&self.notify_handler)))) + } else { + Ok(()) + } + } + + 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 toolchain = try!(self.verify_toolchain(name)); + + Ok(Some(toolchain)) + } + + pub fn find_override(&self, path: &Path) -> Result> { + if let Some(ref name) = self.env_override { + let toolchain = try!(self.verify_toolchain(name)); + + return Ok(Some((toolchain, OverrideReason::Environment))); + } + + if let Some((name, reason_path)) = try!(self.override_db + .find(path, self.notify_handler.as_ref())) { + let toolchain = try!(self.verify_toolchain(&name)); + return Ok(Some((toolchain, OverrideReason::OverrideDB(reason_path)))); + } + + Ok(None) + } + + pub fn find_override_toolchain_or_default + (&self, + path: &Path) + -> Result)>> { + Ok(if let Some((toolchain, reason)) = try!(self.find_override(path)) { + Some((toolchain, Some(reason))) + } else { + try!(self.find_default()).map(|toolchain| (toolchain, None)) + }) + } + + pub fn list_toolchains(&self) -> Result> { + if utils::is_directory(&self.toolchains_dir) { + let toolchains: Vec<_> = try!(utils::read_dir("toolchains", &self.toolchains_dir)) + .filter_map(io::Result::ok) + .filter_map(|e| e.file_name().into_string().ok()) + .collect(); + + Ok(toolchains) + } else { + Ok(Vec::new()) + } + } + + pub fn update_all_channels(&self) -> Result)>> { + let mut toolchains = try!(self.list_toolchains()); + toolchains.sort(); + + Ok(toolchains.into_iter() + .merge(["beta", "nightly", "stable"].into_iter().map(|s| (*s).to_owned())) + .dedup() + .filter(|name| { + dist::ToolchainDesc::from_str(&name).map(|d| d.is_tracking()) == Some(true) + }) + .map(|name| { + let result = self.get_toolchain(&name, true) + .and_then(|t| t.install_from_dist()); + if let Err(ref e) = result { + self.notify_handler.call(Notification::NonFatalError(e)); + } + (name, result) + }) + .collect()) + } + + 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(true) + } else { + let current_version = try!(utils::read_file("metadata version", &self.version_file)); + + self.notify_handler.call(Notification::ReadMetadataVersion(¤t_version)); + + Ok(&*current_version == METADATA_VERSION) + } + } + + pub fn toolchain_for_dir(&self, path: &Path) -> Result<(Toolchain, Option)> { + self.find_override_toolchain_or_default(path) + .and_then(|r| r.ok_or(Error::NoDefaultToolchain)) + } + + pub fn create_command_for_dir(&self, path: &Path, binary: &str) -> Result { + let (toolchain, _) = try!(self.toolchain_for_dir(path)); + toolchain.create_command(binary) + } + + pub fn doc_path_for_dir(&self, path: &Path, relative: &str) -> Result { + let (toolchain, _) = try!(self.toolchain_for_dir(path)); + toolchain.doc_path(relative) + } + + pub fn open_docs_for_dir(&self, path: &Path, relative: &str) -> Result<()> { + let (toolchain, _) = try!(self.toolchain_for_dir(path)); + toolchain.open_docs(relative) + } } diff --git a/src/errors.rs b/src/errors.rs index 5edb47f17c..2ef143f6ec 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -5,39 +5,42 @@ use rust_install::{self, utils, temp}; use rust_install::notify::{self, NotificationLevel, Notifyable}; pub enum Notification<'a> { - Install(rust_install::Notification<'a>), - Utils(utils::Notification<'a>), - Temp(temp::Notification<'a>), - - SetDefaultToolchain(&'a str), - SetOverrideToolchain(&'a Path, &'a str), - LookingForToolchain(&'a str), - ToolchainDirectory(&'a Path, &'a str), - UpdatingToolchain(&'a str), - InstallingToolchain(&'a str), - UsingExistingToolchain(&'a str), - UninstallingToolchain(&'a str), - UninstalledToolchain(&'a str), - ToolchainNotInstalled(&'a str), - - UpgradingMetadata(&'a str, &'a str), - WritingMetadataVersion(&'a str), - ReadMetadataVersion(&'a str), - NonFatalError(&'a Error), + Install(rust_install::Notification<'a>), + Utils(utils::Notification<'a>), + Temp(temp::Notification<'a>), + + SetDefaultToolchain(&'a str), + SetOverrideToolchain(&'a Path, &'a str), + LookingForToolchain(&'a str), + ToolchainDirectory(&'a Path, &'a str), + UpdatingToolchain(&'a str), + InstallingToolchain(&'a str), + UsingExistingToolchain(&'a str), + UninstallingToolchain(&'a str), + UninstalledToolchain(&'a str), + ToolchainNotInstalled(&'a str), + + UpgradingMetadata(&'a str, &'a str), + WritingMetadataVersion(&'a str), + ReadMetadataVersion(&'a str), + NonFatalError(&'a Error), } pub enum Error { - Install(rust_install::Error), - Utils(utils::Error), - Temp(temp::Error), - - UnknownMetadataVersion(String), - InvalidEnvironment, - NoDefaultToolchain, - PermissionDenied, - ToolchainNotInstalled(String), - UnknownHostTriple, - Custom { id: String, desc: String }, + Install(rust_install::Error), + Utils(utils::Error), + Temp(temp::Error), + + UnknownMetadataVersion(String), + InvalidEnvironment, + NoDefaultToolchain, + PermissionDenied, + ToolchainNotInstalled(String), + UnknownHostTriple, + Custom { + id: String, + desc: String, + }, } pub type Result = ::std::result::Result; @@ -53,78 +56,76 @@ extend_notification!(Notification: utils::Notification, n => Notification::Utils extend_notification!(Notification: temp::Notification, n => Notification::Temp(n)); impl<'a> Notification<'a> { - pub fn level(&self) -> NotificationLevel { - use self::Notification::*; - match *self { - Install(ref n) => n.level(), - Utils(ref n) => n.level(), - Temp(ref n) => n.level(), - ToolchainDirectory(_, _) | LookingForToolchain(_) | - WritingMetadataVersion(_) | ReadMetadataVersion(_) => - NotificationLevel::Verbose, - SetDefaultToolchain(_) | SetOverrideToolchain(_, _) | UpdatingToolchain(_) | - InstallingToolchain(_) | UsingExistingToolchain(_) | UninstallingToolchain(_) | - UninstalledToolchain(_) | ToolchainNotInstalled(_) | UpgradingMetadata(_, _) => - NotificationLevel::Info, - NonFatalError(_) => - NotificationLevel::Error, - } - } + pub fn level(&self) -> NotificationLevel { + use self::Notification::*; + match *self { + Install(ref n) => n.level(), + Utils(ref n) => n.level(), + Temp(ref n) => n.level(), + ToolchainDirectory(_, _) | + LookingForToolchain(_) | + WritingMetadataVersion(_) | + ReadMetadataVersion(_) => NotificationLevel::Verbose, + SetDefaultToolchain(_) | + SetOverrideToolchain(_, _) | + UpdatingToolchain(_) | + InstallingToolchain(_) | + UsingExistingToolchain(_) | + UninstallingToolchain(_) | + UninstalledToolchain(_) | + ToolchainNotInstalled(_) | + UpgradingMetadata(_, _) => NotificationLevel::Info, + NonFatalError(_) => NotificationLevel::Error, + } + } } impl<'a> Display for Notification<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> { - use self::Notification::*; - match *self { - Install(ref n) => n.fmt(f), - Utils(ref n) => n.fmt(f), - Temp(ref n) => n.fmt(f), - SetDefaultToolchain(name) => - write!(f, "default toolchain set to '{}'", name), - SetOverrideToolchain(path, name) => - write!(f, "override toolchain for '{}' set to '{}'", path.display(), name), - LookingForToolchain(name) => - write!(f, "looking for installed toolchain '{}'", name), - ToolchainDirectory(path, _) => - write!(f, "toolchain directory: '{}'", path.display()), - UpdatingToolchain(name) => - write!(f, "updating existing install for '{}'", name), - InstallingToolchain(name) => - write!(f, "installing toolchain '{}'", name), - UsingExistingToolchain(name) => - write!(f, "using existing install for '{}'", name), - UninstallingToolchain(name) => - write!(f, "uninstalling toolchain '{}'", name), - UninstalledToolchain(name) => - write!(f, "toolchain '{}' uninstalled", name), - ToolchainNotInstalled(name) => - write!(f, "no toolchain installed for '{}'", name), - UpgradingMetadata(from_ver, to_ver) => - write!(f, "upgrading metadata version from '{}' to '{}'", from_ver, to_ver), - WritingMetadataVersion(ver) => - write!(f, "writing metadata version: '{}'", ver), - ReadMetadataVersion(ver) => - write!(f, "read metadata version: '{}'", ver), - NonFatalError(e) => - write!(f, "{}", e), - } - } + fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> { + use self::Notification::*; + match *self { + Install(ref n) => n.fmt(f), + Utils(ref n) => n.fmt(f), + Temp(ref n) => n.fmt(f), + SetDefaultToolchain(name) => write!(f, "default toolchain set to '{}'", name), + SetOverrideToolchain(path, name) => write!(f, + "override toolchain for '{}' set to '{}'", + path.display(), + name), + LookingForToolchain(name) => write!(f, "looking for installed toolchain '{}'", name), + ToolchainDirectory(path, _) => write!(f, "toolchain directory: '{}'", path.display()), + UpdatingToolchain(name) => write!(f, "updating existing install for '{}'", name), + InstallingToolchain(name) => write!(f, "installing toolchain '{}'", name), + UsingExistingToolchain(name) => write!(f, "using existing install for '{}'", name), + UninstallingToolchain(name) => write!(f, "uninstalling toolchain '{}'", name), + UninstalledToolchain(name) => write!(f, "toolchain '{}' uninstalled", name), + ToolchainNotInstalled(name) => write!(f, "no toolchain installed for '{}'", name), + UpgradingMetadata(from_ver, to_ver) => + write!(f, + "upgrading metadata version from '{}' to '{}'", + from_ver, + to_ver), + WritingMetadataVersion(ver) => write!(f, "writing metadata version: '{}'", ver), + ReadMetadataVersion(ver) => write!(f, "read metadata version: '{}'", ver), + NonFatalError(e) => write!(f, "{}", e), + } + } } impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> { - use self::Error::*; - match *self { - Install(ref n) => n.fmt(f), - Utils(ref n) => n.fmt(f), - Temp(ref n) => n.fmt(f), - UnknownMetadataVersion(ref ver) => write!(f, "unknown metadata version: '{}'", ver), - InvalidEnvironment => write!(f, "invalid environment"), - NoDefaultToolchain => write!(f, "no default toolchain configured"), - PermissionDenied => write!(f, "permission denied"), - ToolchainNotInstalled(ref name) => write!(f, "toolchain '{}' is not installed", name), - UnknownHostTriple => write!(f, "unknown host triple"), - Custom { id: _, ref desc } => write!(f, "{}", desc), - } - } + fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> { + use self::Error::*; + match *self { + Install(ref n) => n.fmt(f), + Utils(ref n) => n.fmt(f), + Temp(ref n) => n.fmt(f), + UnknownMetadataVersion(ref ver) => write!(f, "unknown metadata version: '{}'", ver), + InvalidEnvironment => write!(f, "invalid environment"), + NoDefaultToolchain => write!(f, "no default toolchain configured"), + PermissionDenied => write!(f, "permission denied"), + ToolchainNotInstalled(ref name) => write!(f, "toolchain '{}' is not installed", name), + UnknownHostTriple => write!(f, "unknown host triple"), + Custom { id: _, ref desc } => write!(f, "{}", desc), + } + } } diff --git a/src/override_db.rs b/src/override_db.rs index 8367f1c36f..6e06aa8297 100644 --- a/src/override_db.rs +++ b/src/override_db.rs @@ -8,98 +8,109 @@ pub const DB_DELIMITER: &'static str = ";"; 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 path = &*dir; - while let Some(parent) = path.parent() { - 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() - ))); - } - - 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()) - } - } - + + 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 path = &*dir; + while let Some(parent) = path.parent() { + 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()))); + } + + 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/toolchain.rs b/src/toolchain.rs index 4e18972bef..ed5560ae4d 100644 --- a/src/toolchain.rs +++ b/src/toolchain.rs @@ -12,189 +12,205 @@ use hyper; use rust_install; pub struct Toolchain<'a> { - cfg: &'a Cfg, - name: String, - prefix: InstallPrefix, + cfg: &'a Cfg, + name: String, + prefix: InstallPrefix, } impl<'a> Toolchain<'a> { - pub fn from(cfg: &'a Cfg, name: &str) -> Self { - Toolchain { - cfg: cfg, - name: name.to_owned(), - prefix: InstallPrefix::from(cfg.toolchains_dir.join(name), InstallType::Owned), - } - } - pub fn cfg(&self) -> &'a Cfg { - self.cfg - } - pub fn name(&self) -> &str { - &self.name - } - pub fn prefix(&self) -> &InstallPrefix { - &self.prefix - } - pub fn exists(&self) -> bool { - utils::is_directory(self.prefix.path()) - } - pub fn verify(&self) -> Result<()> { - Ok(try!(utils::assert_is_directory(self.prefix.path()))) - } - pub fn remove(&self) -> Result<()> { - if self.exists() { - self.cfg.notify_handler.call(Notification::UninstallingToolchain(&self.name)); - } else { - self.cfg.notify_handler.call(Notification::ToolchainNotInstalled(&self.name)); - return Ok(()); - } - if let Some(update_hash) = try!(self.update_hash()) { - try!(utils::remove_file("update hash", &update_hash)); - } - let handler = self.cfg.notify_handler.as_ref(); - let result = self.prefix.uninstall(ntfy!(&handler)); - if !self.exists() { - self.cfg.notify_handler.call(Notification::UninstalledToolchain(&self.name)); - } - Ok(try!(result)) - } - pub fn remove_if_exists(&self) -> Result<()> { - if self.exists() { - self.remove() - } else { - Ok(()) - } - } - pub fn install(&self, install_method: InstallMethod) -> Result<()> { - if self.exists() { - self.cfg.notify_handler.call(Notification::UpdatingToolchain(&self.name)); - } else { - self.cfg.notify_handler.call(Notification::InstallingToolchain(&self.name)); - } - self.cfg.notify_handler.call(Notification::ToolchainDirectory(self.prefix.path(), &self.name)); - let handler = self.cfg.notify_handler.as_ref(); - Ok(try!(self.prefix.install(install_method, ntfy!(&handler)))) - } - pub fn install_if_not_installed(&self, install_method: InstallMethod) -> Result<()> { - self.cfg.notify_handler.call(Notification::LookingForToolchain(&self.name)); - if !self.exists() { - self.install(install_method) - } else { - self.cfg.notify_handler.call(Notification::UsingExistingToolchain(&self.name)); - Ok(()) - } - } - pub fn update_hash(&self) -> Result> { - if self.is_custom() { - Ok(None) - } else { - Ok(Some(try!(self.cfg.get_hash_file(&self.name, true)))) - } - } - - fn download_cfg(&self) -> dist::DownloadCfg { - dist::DownloadCfg { - dist_root: &self.cfg.dist_root_url, - temp_cfg: &self.cfg.temp_cfg, - notify_handler: ntfy!(&self.cfg.notify_handler), - } - } - - pub fn install_from_dist(&self) -> Result<()> { - let update_hash = try!(self.update_hash()); - self.install(InstallMethod::Dist(&self.name, update_hash.as_ref().map(|p| &**p), self.download_cfg())) - } - pub fn install_from_dist_if_not_installed(&self) -> Result<()> { - let update_hash = try!(self.update_hash()); - self.install_if_not_installed(InstallMethod::Dist(&self.name, update_hash.as_ref().map(|p| &**p), self.download_cfg())) - } - pub fn is_custom(&self) -> bool { - dist::ToolchainDesc::from_str(&self.name).is_none() - } - pub fn is_tracking(&self) -> bool { - dist::ToolchainDesc::from_str(&self.name).map(|d| d.is_tracking()) == Some(true) - } - - pub fn ensure_custom(&self) -> Result<()> { - if !self.is_custom() { - Err(Error::Install(rust_install::Error::InvalidToolchainName)) - } else { - Ok(()) - } - } - - pub fn install_from_installers(&self, installers: &[&OsStr]) -> Result<()> { - try!(self.ensure_custom()); - - try!(self.remove_if_exists()); - - let work_dir = try!(self.cfg.temp_cfg.new_directory()); - - for installer in installers { - let local_installer; - let installer_str = installer.to_str(); - if let Some(Ok(url)) = installer_str.map(hyper::Url::parse) { - // If installer is a URL - - // Extract basename from url (eg. 'rust-1.3.0-x86_64-unknown-linux-gnu.tar.gz') - let re = Regex::new(r"[\\/]([^\\/?]+)(\?.*)?$").unwrap(); - let basename = try!(re.captures(installer_str.unwrap()) - .ok_or(Error::Utils(utils::Error::InvalidUrl { url: installer_str.unwrap().to_owned() }))).at(1).unwrap(); - - // Download to a local file - local_installer = Cow::Owned(work_dir.join(basename)); - try!(utils::download_file(url, &local_installer, None, ntfy!(&self.cfg.notify_handler))); - } else { - // If installer is a filename - - // No need to download - local_installer = Cow::Borrowed(Path::new(installer)); - } - - // Install from file - try!(self.install(InstallMethod::Installer(&local_installer, &self.cfg.temp_cfg))); - } - - Ok(()) - } - - pub fn install_from_dir(&self, src: &Path, link: bool) -> Result<()> { - if link { - self.install(InstallMethod::Link(&try!(utils::to_absolute(src)))) - } else { - self.install(InstallMethod::Copy(src)) - } - } - - pub fn set_env(&self, cmd: &mut Command) { - self.prefix.set_env(cmd); - cmd.env("MULTIRUST_TOOLCHAIN", self.prefix.path()); - cmd.env("MULTIRUST_HOME", &self.cfg.multirust_dir); - } - - pub fn create_command(&self, binary: &str) -> Result { - if !self.exists() { - return Err(Error::ToolchainNotInstalled(self.name.to_owned())); - } - - let binary_path = self.prefix.binary_file(binary); - let mut cmd = Command::new(binary_path); - self.set_env(&mut cmd); - Ok(cmd) - } - - pub fn doc_path(&self, relative: &str) -> Result { - try!(self.verify()); - Ok(try!(self.prefix.doc_path(relative))) - } - pub fn open_docs(&self, relative: &str) -> Result<()> { - try!(self.verify()); - Ok(try!(self.prefix.open_docs(relative))) - } - - pub fn make_default(&self) -> Result<()> { - 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()))) - } + pub fn from(cfg: &'a Cfg, name: &str) -> Self { + Toolchain { + cfg: cfg, + name: name.to_owned(), + prefix: InstallPrefix::from(cfg.toolchains_dir.join(name), InstallType::Owned), + } + } + pub fn cfg(&self) -> &'a Cfg { + self.cfg + } + pub fn name(&self) -> &str { + &self.name + } + pub fn prefix(&self) -> &InstallPrefix { + &self.prefix + } + pub fn exists(&self) -> bool { + utils::is_directory(self.prefix.path()) + } + pub fn verify(&self) -> Result<()> { + Ok(try!(utils::assert_is_directory(self.prefix.path()))) + } + pub fn remove(&self) -> Result<()> { + if self.exists() { + self.cfg.notify_handler.call(Notification::UninstallingToolchain(&self.name)); + } else { + self.cfg.notify_handler.call(Notification::ToolchainNotInstalled(&self.name)); + return Ok(()); + } + if let Some(update_hash) = try!(self.update_hash()) { + try!(utils::remove_file("update hash", &update_hash)); + } + let handler = self.cfg.notify_handler.as_ref(); + let result = self.prefix.uninstall(ntfy!(&handler)); + if !self.exists() { + self.cfg.notify_handler.call(Notification::UninstalledToolchain(&self.name)); + } + Ok(try!(result)) + } + pub fn remove_if_exists(&self) -> Result<()> { + if self.exists() { + self.remove() + } else { + Ok(()) + } + } + pub fn install(&self, install_method: InstallMethod) -> Result<()> { + if self.exists() { + self.cfg.notify_handler.call(Notification::UpdatingToolchain(&self.name)); + } else { + self.cfg.notify_handler.call(Notification::InstallingToolchain(&self.name)); + } + self.cfg + .notify_handler + .call(Notification::ToolchainDirectory(self.prefix.path(), &self.name)); + let handler = self.cfg.notify_handler.as_ref(); + Ok(try!(self.prefix.install(install_method, ntfy!(&handler)))) + } + pub fn install_if_not_installed(&self, install_method: InstallMethod) -> Result<()> { + self.cfg.notify_handler.call(Notification::LookingForToolchain(&self.name)); + if !self.exists() { + self.install(install_method) + } else { + self.cfg.notify_handler.call(Notification::UsingExistingToolchain(&self.name)); + Ok(()) + } + } + pub fn update_hash(&self) -> Result> { + if self.is_custom() { + Ok(None) + } else { + Ok(Some(try!(self.cfg.get_hash_file(&self.name, true)))) + } + } + + fn download_cfg(&self) -> dist::DownloadCfg { + dist::DownloadCfg { + dist_root: &self.cfg.dist_root_url, + temp_cfg: &self.cfg.temp_cfg, + notify_handler: ntfy!(&self.cfg.notify_handler), + } + } + + pub fn install_from_dist(&self) -> Result<()> { + let update_hash = try!(self.update_hash()); + self.install(InstallMethod::Dist(&self.name, + update_hash.as_ref().map(|p| &**p), + self.download_cfg())) + } + pub fn install_from_dist_if_not_installed(&self) -> Result<()> { + let update_hash = try!(self.update_hash()); + self.install_if_not_installed(InstallMethod::Dist(&self.name, + update_hash.as_ref().map(|p| &**p), + self.download_cfg())) + } + pub fn is_custom(&self) -> bool { + dist::ToolchainDesc::from_str(&self.name).is_none() + } + pub fn is_tracking(&self) -> bool { + dist::ToolchainDesc::from_str(&self.name).map(|d| d.is_tracking()) == Some(true) + } + + pub fn ensure_custom(&self) -> Result<()> { + if !self.is_custom() { + Err(Error::Install(rust_install::Error::InvalidToolchainName)) + } else { + Ok(()) + } + } + + pub fn install_from_installers(&self, installers: &[&OsStr]) -> Result<()> { + try!(self.ensure_custom()); + + try!(self.remove_if_exists()); + + let work_dir = try!(self.cfg.temp_cfg.new_directory()); + + for installer in installers { + let local_installer; + let installer_str = installer.to_str(); + if let Some(Ok(url)) = installer_str.map(hyper::Url::parse) { + // If installer is a URL + + // Extract basename from url (eg. 'rust-1.3.0-x86_64-unknown-linux-gnu.tar.gz') + let re = Regex::new(r"[\\/]([^\\/?]+)(\?.*)?$").unwrap(); + let basename = try!(re.captures(installer_str.unwrap()) + .ok_or(Error::Utils(utils::Error::InvalidUrl { + url: installer_str.unwrap().to_owned(), + }))) + .at(1) + .unwrap(); + + // Download to a local file + local_installer = Cow::Owned(work_dir.join(basename)); + try!(utils::download_file(url, + &local_installer, + None, + ntfy!(&self.cfg.notify_handler))); + } else { + // If installer is a filename + + // No need to download + local_installer = Cow::Borrowed(Path::new(installer)); + } + + // Install from file + try!(self.install(InstallMethod::Installer(&local_installer, &self.cfg.temp_cfg))); + } + + Ok(()) + } + + pub fn install_from_dir(&self, src: &Path, link: bool) -> Result<()> { + if link { + self.install(InstallMethod::Link(&try!(utils::to_absolute(src)))) + } else { + self.install(InstallMethod::Copy(src)) + } + } + + pub fn set_env(&self, cmd: &mut Command) { + self.prefix.set_env(cmd); + cmd.env("MULTIRUST_TOOLCHAIN", self.prefix.path()); + cmd.env("MULTIRUST_HOME", &self.cfg.multirust_dir); + } + + pub fn create_command(&self, binary: &str) -> Result { + if !self.exists() { + return Err(Error::ToolchainNotInstalled(self.name.to_owned())); + } + + let binary_path = self.prefix.binary_file(binary); + let mut cmd = Command::new(binary_path); + self.set_env(&mut cmd); + Ok(cmd) + } + + pub fn doc_path(&self, relative: &str) -> Result { + try!(self.verify()); + Ok(try!(self.prefix.doc_path(relative))) + } + pub fn open_docs(&self, relative: &str) -> Result<()> { + try!(self.verify()); + Ok(try!(self.prefix.open_docs(relative))) + } + + pub fn make_default(&self) -> Result<()> { + 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()))) + } }