diff --git a/Cargo.lock b/Cargo.lock index a0a097ba21b..c0d530e2f14 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,6 +10,7 @@ dependencies = [ "curl 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "docopt 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "flate2 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)", "fs2 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -65,6 +66,29 @@ dependencies = [ "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "backtrace" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gcc 0.3.49 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "bitflags" version = "0.7.0" @@ -121,6 +145,7 @@ name = "crates-io" version = "0.9.0" dependencies = [ "curl 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -157,6 +182,15 @@ dependencies = [ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "dbghelp-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "docopt" version = "0.7.0" @@ -182,6 +216,14 @@ dependencies = [ "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "error-chain" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "filetime" version = "0.1.10" @@ -559,6 +601,11 @@ name = "regex-syntax" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "rustc-demangle" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "rustc-serialize" version = "0.3.24" @@ -800,6 +847,8 @@ dependencies = [ "checksum advapi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e06588080cb19d0acb6739808aafa5f26bfb2ca015b2b6370028b44cf7cb8a9a" "checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66" "checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699" +"checksum backtrace 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "72f9b4182546f4b04ebc4ab7f84948953a118bd6021a1b6a6c909e3e94f6be76" +"checksum backtrace-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d192fd129132fbc97497c1f2ec2c2c5174e376b95f535199ef4fe0a293d33842" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4" "checksum bufstream 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f2f382711e76b9de6c744cc00d0497baba02fb00a787f088c879f01d09468e32" @@ -808,9 +857,11 @@ dependencies = [ "checksum crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0c5ea215664ca264da8a9d9c3be80d2eaf30923c259d03e870388eb927508f97" "checksum curl 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c90e1240ef340dd4027ade439e5c7c2064dd9dc652682117bd50d1486a3add7b" "checksum curl-sys 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "23e7e544dc5e1ba42c4a4a678bd47985e84b9c3f4d3404c29700622a029db9c3" +"checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850" "checksum docopt 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab32ea6e284d87987066f21a9e809a73c14720571ef34516f0890b3d355ccfd8" "checksum dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80c8b71fd71146990a9742fc06dcbbde19161a267e0ad4e572c35162f4578c90" "checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b" +"checksum error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9435d864e017c3c6afeac1654189b06cdb491cf2ff73dbf0d73b0f292f42ff8" "checksum filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "5363ab8e4139b8568a6237db5248646e5a8a2f89bd5ccb02092182b11fd3e922" "checksum flate2 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)" = "36df0166e856739905cd3d7e0b210fe818592211a008862599845e012d8d304c" "checksum foreign-types 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e4056b9bd47f8ac5ba12be771f77a0dae796d1bbaaf5fd0b9c2d38b69b8a29d" @@ -855,6 +906,7 @@ dependencies = [ "checksum regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1731164734096285ec2a5ec7fea5248ae2f5485b3feeb0115af4fda2183b2d1b" "checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957" "checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db" +"checksum rustc-demangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3058a43ada2c2d0b92b3ae38007a2d0fa5e9db971be260e0171408a4ff471c95" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum semver 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3fdd61b85a0fa777f7fb7c454b9189b2941b110d1385ce84d7f76efdf1606a85" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" diff --git a/Cargo.toml b/Cargo.toml index 0a7ca98fade..c8df5d84f5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ crossbeam = "0.2" curl = "0.4.6" docopt = "0.7" env_logger = "0.4" +error-chain = "0.10.0" filetime = "0.1" flate2 = "0.2" fs2 = "0.4" diff --git a/src/bin/bench.rs b/src/bin/bench.rs index ba5a716dd35..c58ff5c087d 100644 --- a/src/bin/bench.rs +++ b/src/bin/bench.rs @@ -1,6 +1,6 @@ use cargo::core::Workspace; use cargo::ops::{self, MessageFormat, Packages}; -use cargo::util::{CliResult, CliError, Human, Config, human}; +use cargo::util::{CliResult, CliError, Config, CargoErrorKind}; use cargo::util::important_paths::{find_root_manifest_for_wd}; #[derive(RustcDecodable)] @@ -128,8 +128,8 @@ pub fn execute(options: Options, config: &Config) -> CliResult { None => Ok(()), Some(err) => { Err(match err.exit.as_ref().and_then(|e| e.code()) { - Some(i) => CliError::new(human("bench failed"), i), - None => CliError::new(Box::new(Human(err)), 101) + Some(i) => CliError::new("bench failed".into(), i), + None => CliError::new(CargoErrorKind::CargoTestErrorKind(err).into(), 101) }) } } diff --git a/src/bin/cargo.rs b/src/bin/cargo.rs index 1b8c7a9c3d1..09f03ef2018 100644 --- a/src/bin/cargo.rs +++ b/src/bin/cargo.rs @@ -17,7 +17,7 @@ use std::fs; use std::path::{Path, PathBuf}; use cargo::core::shell::{Verbosity, ColorConfig}; -use cargo::util::{self, CliResult, lev_distance, Config, human, CargoResult}; +use cargo::util::{self, CliResult, lev_distance, Config, CargoResult, CargoError, CargoErrorKind}; use cargo::util::CliError; #[derive(RustcDecodable)] @@ -84,7 +84,9 @@ fn main() { let result = (|| { let args: Vec<_> = try!(env::args_os() .map(|s| { - s.into_string().map_err(|s| human(format!("invalid unicode in argument: {:?}", s))) + s.into_string().map_err(|s| { + CargoError::from(format!("invalid unicode in argument: {:?}", s)) + }) }) .collect()); let rest = &args; @@ -180,7 +182,7 @@ fn execute(flags: Flags, config: &Config) -> CliResult { if let Some(ref code) = flags.flag_explain { let mut procss = config.rustc()?.process(); - procss.arg("--explain").arg(code).exec().map_err(human)?; + procss.arg("--explain").arg(code).exec()?; return Ok(()); } @@ -309,7 +311,7 @@ fn execute_external_subcommand(config: &Config, cmd: &str, args: &[String]) -> C let command = match path { Some(command) => command, None => { - return Err(human(match find_closest(config, cmd) { + return Err(CargoError::from(match find_closest(config, cmd) { Some(closest) => { format!("no such subcommand: `{}`\n\n\tDid you mean `{}`?\n", cmd, @@ -330,11 +332,12 @@ fn execute_external_subcommand(config: &Config, cmd: &str, args: &[String]) -> C Err(e) => e, }; - if let Some(code) = err.exit.as_ref().and_then(|c| c.code()) { - Err(CliError::code(code)) - } else { - Err(CliError::new(Box::new(err), 101)) + if let &CargoErrorKind::ProcessErrorKind(ref perr) = err.kind() { + if let Some(code) = perr.exit.as_ref().and_then(|c| c.code()) { + return Err(CliError::code(code)); + } } + Err(CliError::new(err, 101)) } /// List all runnable commands diff --git a/src/bin/help.rs b/src/bin/help.rs index a068b0a2c63..f59e24f3ebd 100644 --- a/src/bin/help.rs +++ b/src/bin/help.rs @@ -1,4 +1,4 @@ -use cargo::util::{CliResult, CliError, Config, human}; +use cargo::util::{CliResult, CliError, Config}; #[derive(RustcDecodable)] pub struct Options; @@ -18,5 +18,5 @@ pub fn execute(_: Options, _: &Config) -> CliResult { // This is a dummy command just so that `cargo help help` works. // The actual delegation of help flag to subcommands is handled by the // cargo command. - Err(CliError::new(human("help command should not be executed directly"), 101)) + Err(CliError::new("help command should not be executed directly".into(), 101)) } diff --git a/src/bin/locate_project.rs b/src/bin/locate_project.rs index 810be467c0a..7c7727aa221 100644 --- a/src/bin/locate_project.rs +++ b/src/bin/locate_project.rs @@ -1,5 +1,5 @@ use cargo; -use cargo::util::{CliResult, CliError, human, ChainError, Config}; +use cargo::util::{CliResult, CliError, Config}; use cargo::util::important_paths::{find_root_manifest_for_wd}; #[derive(RustcDecodable)] @@ -28,9 +28,9 @@ pub fn execute(flags: LocateProjectFlags, let root = find_root_manifest_for_wd(flags.flag_manifest_path, config.cwd())?; let string = root.to_str() - .chain_error(|| human("Your project path contains \ + .ok_or_else(|| "Your project path contains \ characters not representable in \ - Unicode")) + Unicode".into()) .map_err(|e| CliError::new(e, 1))?; let location = ProjectLocation { root: string.to_string() }; diff --git a/src/bin/login.rs b/src/bin/login.rs index 7e575f47df4..6e7841717b6 100644 --- a/src/bin/login.rs +++ b/src/bin/login.rs @@ -4,7 +4,7 @@ use std::io; use cargo::ops; use cargo::core::{SourceId, Source}; use cargo::sources::RegistrySource; -use cargo::util::{CliResult, Config, human, ChainError}; +use cargo::util::{CliResult, CargoResultExt, Config}; #[derive(RustcDecodable)] pub struct Options { @@ -51,8 +51,8 @@ pub fn execute(options: Options, config: &Config) -> CliResult { println!("please visit {}me and paste the API Token below", host); let mut line = String::new(); let input = io::stdin(); - input.lock().read_line(&mut line).chain_error(|| { - human("failed to read stdin") + input.lock().read_line(&mut line).chain_err(|| { + "failed to read stdin" })?; line } diff --git a/src/bin/run.rs b/src/bin/run.rs index 6d66d26ee4e..96c51bea240 100644 --- a/src/bin/run.rs +++ b/src/bin/run.rs @@ -2,7 +2,7 @@ use std::iter::FromIterator; use cargo::core::Workspace; use cargo::ops::{self, MessageFormat, Packages}; -use cargo::util::{CliResult, CliError, Config, Human}; +use cargo::util::{CliResult, CliError, Config, CargoErrorKind}; use cargo::util::important_paths::{find_root_manifest_for_wd}; #[derive(RustcDecodable)] @@ -113,7 +113,8 @@ pub fn execute(options: Options, config: &Config) -> CliResult { // bad and we always want to forward that up. let exit = match err.exit.clone() { Some(exit) => exit, - None => return Err(CliError::new(Box::new(Human(err)), 101)), + None => return Err( + CliError::new(CargoErrorKind::ProcessErrorKind(err).into(), 101)), }; // If `-q` was passed then we suppress extra error information about @@ -123,7 +124,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult { Err(if options.flag_quiet == Some(true) { CliError::code(exit_code) } else { - CliError::new(Box::new(Human(err)), exit_code) + CliError::new(CargoErrorKind::ProcessErrorKind(err).into(), exit_code) }) } } diff --git a/src/bin/rustc.rs b/src/bin/rustc.rs index 8805c8f8a4e..3ee18a86234 100644 --- a/src/bin/rustc.rs +++ b/src/bin/rustc.rs @@ -3,7 +3,7 @@ use std::env; use cargo::core::Workspace; use cargo::ops::{self, CompileOptions, CompileMode, MessageFormat, Packages}; use cargo::util::important_paths::{find_root_manifest_for_wd}; -use cargo::util::{CliResult, CliError, Config, human}; +use cargo::util::{CliResult, CliError, Config}; #[derive(RustcDecodable)] pub struct Options { @@ -98,8 +98,8 @@ pub fn execute(options: Options, config: &Config) -> CliResult { Some("bench") => CompileMode::Bench, Some("check") => CompileMode::Check, Some(mode) => { - let err = human(format!("unknown profile: `{}`, use dev, - test, or bench", mode)); + let err = format!("unknown profile: `{}`, use dev, + test, or bench", mode).into(); return Err(CliError::new(err, 101)) } }; diff --git a/src/bin/test.rs b/src/bin/test.rs index 7829cb36601..5fbae53f373 100644 --- a/src/bin/test.rs +++ b/src/bin/test.rs @@ -1,6 +1,6 @@ use cargo::core::Workspace; use cargo::ops::{self, MessageFormat, Packages}; -use cargo::util::{CliResult, CliError, Human, human, Config}; +use cargo::util::{CliResult, CliError, Config, CargoErrorKind}; use cargo::util::important_paths::find_root_manifest_for_wd; #[derive(RustcDecodable)] @@ -163,8 +163,8 @@ pub fn execute(options: Options, config: &Config) -> CliResult { None => Ok(()), Some(err) => { Err(match err.exit.as_ref().and_then(|e| e.code()) { - Some(i) => CliError::new(human(err.hint()), i), - None => CliError::new(Box::new(Human(err)), 101), + Some(i) => CliError::new(err.hint().into(), i), + None => CliError::new(CargoErrorKind::CargoTestErrorKind(err).into(), 101), }) } } diff --git a/src/cargo/core/dependency.rs b/src/cargo/core/dependency.rs index 9401eeccbb0..3b75934ebb5 100644 --- a/src/cargo/core/dependency.rs +++ b/src/cargo/core/dependency.rs @@ -7,7 +7,8 @@ use semver::ReqParseError; use serde::ser; use core::{SourceId, Summary, PackageId}; -use util::{CargoError, CargoResult, Cfg, CfgExpr, ChainError, human, Config}; +use util::{Cfg, CfgExpr, Config}; +use util::errors::{CargoResult, CargoResultExt, CargoError}; /// Information about a dependency requested by a Cargo manifest. /// Cheap to copy. @@ -145,7 +146,7 @@ this warning. Ok(requirement) } - e => Err(From::from(e)), + e => Err(e.into()), } }, Ok(v) => Ok(v), @@ -361,13 +362,13 @@ impl ser::Serialize for Platform { } impl FromStr for Platform { - type Err = Box; + type Err = CargoError; fn from_str(s: &str) -> CargoResult { if s.starts_with("cfg(") && s.ends_with(")") { let s = &s[4..s.len()-1]; - s.parse().map(Platform::Cfg).chain_error(|| { - human(format!("failed to parse `{}` as a cfg expression", s)) + s.parse().map(Platform::Cfg).chain_err(|| { + format!("failed to parse `{}` as a cfg expression", s) }) } else { Ok(Platform::Name(s.to_string())) diff --git a/src/cargo/core/package.rs b/src/cargo/core/package.rs index 943f570d335..bdb7f41d4c3 100644 --- a/src/cargo/core/package.rs +++ b/src/cargo/core/package.rs @@ -11,7 +11,8 @@ use toml; use core::{Dependency, Manifest, PackageId, SourceId, Target}; use core::{Summary, SourceMap}; use ops; -use util::{CargoResult, Config, LazyCell, ChainError, internal, human, lev_distance}; +use util::{Config, LazyCell, internal, lev_distance}; +use util::errors::{CargoResult, CargoResultExt}; /// Information about a package that is available somewhere in the file system. /// @@ -181,7 +182,7 @@ impl<'cfg> PackageSet<'cfg> { } pub fn get(&self, id: &PackageId) -> CargoResult<&Package> { - let slot = self.packages.iter().find(|p| p.0 == *id).chain_error(|| { + let slot = self.packages.iter().find(|p| p.0 == *id).ok_or_else(|| { internal(format!("couldn't find `{}` in package set", id)) })?; let slot = &slot.1; @@ -189,11 +190,11 @@ impl<'cfg> PackageSet<'cfg> { return Ok(pkg) } let mut sources = self.sources.borrow_mut(); - let source = sources.get_mut(id.source_id()).chain_error(|| { + let source = sources.get_mut(id.source_id()).ok_or_else(|| { internal(format!("couldn't find source for `{}`", id)) })?; - let pkg = source.download(id).chain_error(|| { - human("unable to get packages from source") + let pkg = source.download(id).chain_err(|| { + "unable to get packages from source" })?; assert!(slot.fill(pkg).is_ok()); Ok(slot.borrow().unwrap()) diff --git a/src/cargo/core/package_id.rs b/src/cargo/core/package_id.rs index f7d67300a9f..1e906d97d16 100644 --- a/src/cargo/core/package_id.rs +++ b/src/cargo/core/package_id.rs @@ -1,5 +1,4 @@ use std::cmp::Ordering; -use std::error::Error; use std::fmt::{self, Formatter}; use std::hash::Hash; use std::hash; @@ -9,7 +8,7 @@ use semver; use serde::de; use serde::ser; -use util::{CargoResult, CargoError, ToSemver}; +use util::{CargoResult, ToSemver}; use core::source::SourceId; /// Identifier for a specific version of a package in a specific source. @@ -96,41 +95,10 @@ impl Ord for PackageId { } } -#[derive(Clone, Debug, PartialEq)] -pub enum PackageIdError { - InvalidVersion(String), - InvalidNamespace(String) -} - -impl Error for PackageIdError { - fn description(&self) -> &str { "failed to parse package id" } -} - -impl fmt::Display for PackageIdError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - PackageIdError::InvalidVersion(ref v) => { - write!(f, "invalid version: {}", *v) - } - PackageIdError::InvalidNamespace(ref ns) => { - write!(f, "invalid namespace: {}", *ns) - } - } - } -} - -impl CargoError for PackageIdError { - fn is_human(&self) -> bool { true } -} - -impl From for Box { - fn from(t: PackageIdError) -> Box { Box::new(t) } -} - impl PackageId { pub fn new(name: &str, version: T, sid: &SourceId) -> CargoResult { - let v = version.to_semver().map_err(PackageIdError::InvalidVersion)?; + let v = version.to_semver()?; Ok(PackageId { inner: Arc::new(PackageIdInner { name: name.to_string(), diff --git a/src/cargo/core/package_id_spec.rs b/src/cargo/core/package_id_spec.rs index 2af87fb6c01..c24b48a365d 100644 --- a/src/cargo/core/package_id_spec.rs +++ b/src/cargo/core/package_id_spec.rs @@ -5,7 +5,8 @@ use semver::Version; use url::Url; use core::PackageId; -use util::{CargoResult, ToUrl, human, ToSemver, ChainError}; +use util::{ToUrl, ToSemver}; +use util::errors::{CargoError, CargoResult, CargoResultExt}; #[derive(Clone, PartialEq, Eq, Debug)] pub struct PackageIdSpec { @@ -29,7 +30,7 @@ impl PackageIdSpec { let mut parts = spec.splitn(2, ':'); let name = parts.next().unwrap(); let version = match parts.next() { - Some(version) => Some(Version::parse(version).map_err(human)?), + Some(version) => Some(Version::parse(version)?), None => None, }; for ch in name.chars() { @@ -47,8 +48,8 @@ impl PackageIdSpec { pub fn query_str<'a, I>(spec: &str, i: I) -> CargoResult<&'a PackageId> where I: IntoIterator { - let spec = PackageIdSpec::parse(spec).chain_error(|| { - human(format!("invalid package id specification: `{}`", spec)) + let spec = PackageIdSpec::parse(spec).chain_err(|| { + format!("invalid package id specification: `{}`", spec) })?; spec.query(i) } @@ -68,12 +69,12 @@ impl PackageIdSpec { let frag = url.fragment().map(|s| s.to_owned()); url.set_fragment(None); let (name, version) = { - let mut path = url.path_segments().chain_error(|| { - human(format!("pkgid urls must have a path: {}", url)) + let mut path = url.path_segments().ok_or_else(|| { + CargoError::from(format!("pkgid urls must have a path: {}", url)) })?; - let path_name = path.next_back().chain_error(|| { - human(format!("pkgid urls must have at least one path \ - component: {}", url)) + let path_name = path.next_back().ok_or_else(|| { + CargoError::from(format!("pkgid urls must have at least one path \ + component: {}", url)) })?; match frag { Some(fragment) => { @@ -81,7 +82,7 @@ impl PackageIdSpec { let name_or_version = parts.next().unwrap(); match parts.next() { Some(part) => { - let version = part.to_semver().map_err(human)?; + let version = part.to_semver()?; (name_or_version.to_string(), Some(version)) } None => { @@ -89,8 +90,7 @@ impl PackageIdSpec { .is_alphabetic() { (name_or_version.to_string(), None) } else { - let version = name_or_version.to_semver() - .map_err(human)?; + let version = name_or_version.to_semver()?; (path_name.to_string(), Some(version)) } } @@ -149,7 +149,7 @@ impl PackageIdSpec { let mut vec = vec![ret, other]; vec.extend(ids); minimize(&mut msg, vec, self); - Err(human(msg)) + Err(msg.into()) } None => Ok(ret) }; diff --git a/src/cargo/core/registry.rs b/src/cargo/core/registry.rs index 292a662ceab..04042b442e2 100644 --- a/src/cargo/core/registry.rs +++ b/src/cargo/core/registry.rs @@ -2,7 +2,8 @@ use std::collections::HashMap; use core::{Source, SourceId, SourceMap, Summary, Dependency, PackageId, Package}; use core::PackageSet; -use util::{CargoResult, ChainError, Config, human, profile}; +use util::{Config, profile}; +use util::errors::{CargoResult, CargoResultExt}; use sources::config::SourceConfigMap; /// Source of information about a group of packages. @@ -189,7 +190,7 @@ impl<'cfg> PackageRegistry<'cfg> { // Ensure the source has fetched all necessary remote data. let _p = profile::start(format!("updating: {}", source_id)); self.sources.get_mut(source_id).unwrap().update() - }).chain_error(|| human(format!("Unable to update {}", source_id))) + })().chain_err(|| format!("Unable to update {}", source_id)) } fn query_overrides(&mut self, dep: &Dependency) @@ -336,9 +337,9 @@ http://doc.crates.io/specifying-dependencies.html#overriding-dependencies impl<'cfg> Registry for PackageRegistry<'cfg> { fn query(&mut self, dep: &Dependency) -> CargoResult> { // Ensure the requested source_id is loaded - self.ensure_loaded(dep.source_id(), Kind::Normal).chain_error(|| { - human(format!("failed to load source for a dependency \ - on `{}`", dep.name())) + self.ensure_loaded(dep.source_id(), Kind::Normal).chain_err(|| { + format!("failed to load source for a dependency \ + on `{}`", dep.name()) })?; let override_summary = self.query_overrides(&dep)?; diff --git a/src/cargo/core/resolver/encode.rs b/src/cargo/core/resolver/encode.rs index 61f8daa52fe..403c01e51c4 100644 --- a/src/cargo/core/resolver/encode.rs +++ b/src/cargo/core/resolver/encode.rs @@ -6,7 +6,8 @@ use serde::ser; use serde::de; use core::{Package, PackageId, SourceId, Workspace}; -use util::{CargoResult, Graph, Config, internal, ChainError, CargoError}; +use util::{Graph, Config, internal}; +use util::errors::{CargoResult, CargoResultExt, CargoError}; use super::Resolve; @@ -132,7 +133,7 @@ impl EncodableResolve { for (k, v) in metadata.iter().filter(|p| p.0.starts_with(prefix)) { to_remove.push(k.to_string()); let k = &k[prefix.len()..]; - let enc_id: EncodablePackageId = k.parse().chain_error(|| { + let enc_id: EncodablePackageId = k.parse().chain_err(|| { internal("invalid encoding of checksum in lockfile") })?; let id = match lookup_id(&enc_id) { @@ -235,12 +236,12 @@ impl fmt::Display for EncodablePackageId { } impl FromStr for EncodablePackageId { - type Err = Box; + type Err = CargoError; fn from_str(s: &str) -> CargoResult { let mut s = s.splitn(3, ' '); let name = s.next().unwrap(); - let version = s.next().chain_error(|| { + let version = s.next().ok_or_else(|| { internal("invalid serialized PackageId") })?; let source_id = match s.next() { diff --git a/src/cargo/core/resolver/mod.rs b/src/cargo/core/resolver/mod.rs index 896b280469e..987fac1e48f 100644 --- a/src/cargo/core/resolver/mod.rs +++ b/src/cargo/core/resolver/mod.rs @@ -56,9 +56,9 @@ use semver; use core::{PackageId, Registry, SourceId, Summary, Dependency}; use core::PackageIdSpec; -use util::{CargoResult, Graph, human, CargoError}; +use util::Graph; +use util::errors::{CargoResult, CargoError}; use util::profile; -use util::ChainError; use util::graph::{Nodes, Edges}; pub use self::encode::{EncodableResolve, EncodableDependency, EncodablePackageId}; @@ -600,7 +600,7 @@ fn activation_error(cx: &Context, parent: &Summary, dep: &Dependency, prev_active: &[Rc], - candidates: &[Candidate]) -> Box { + candidates: &[Candidate]) -> CargoError { if candidates.len() > 0 { let mut msg = format!("failed to select a version for `{}` \ (required by `{}`):\n\ @@ -633,7 +633,7 @@ fn activation_error(cx: &Context, .collect::>() .join(", "))); - return human(msg) + return msg.into() } // Once we're all the way down here, we're definitely lost in the @@ -696,7 +696,7 @@ fn activation_error(cx: &Context, dep.version_req()) }; - human(msg) + msg.into() } // Returns if `a` and `b` are compatible in the semver sense. This is a @@ -887,11 +887,11 @@ impl<'a> Context<'a> { debug!("found an override for {} {}", dep.name(), dep.version_req()); let mut summaries = registry.query(dep)?.into_iter(); - let s = summaries.next().chain_error(|| { - human(format!("no matching package for override `{}` found\n\ - location searched: {}\n\ - version required: {}", - spec, dep.source_id(), dep.version_req())) + let s = summaries.next().ok_or_else(|| { + format!("no matching package for override `{}` found\n\ + location searched: {}\n\ + version required: {}", + spec, dep.source_id(), dep.version_req()) })?; let summaries = summaries.collect::>(); if summaries.len() > 0 { diff --git a/src/cargo/core/source.rs b/src/cargo/core/source.rs index 75f83710c7d..9f32d692dfe 100644 --- a/src/cargo/core/source.rs +++ b/src/cargo/core/source.rs @@ -16,7 +16,7 @@ use ops; use sources::git; use sources::{PathSource, GitSource, RegistrySource, CRATES_IO}; use sources::DirectorySource; -use util::{human, Config, CargoResult, ToUrl}; +use util::{Config, CargoResult, ToUrl}; /// A Source finds and downloads remote packages based on names and /// versions. @@ -138,7 +138,7 @@ impl SourceId { pub fn from_url(string: &str) -> CargoResult { let mut parts = string.splitn(2, '+'); let kind = parts.next().unwrap(); - let url = parts.next().ok_or(human(format!("invalid source `{}`", string)))?; + let url = parts.next().ok_or_else(|| format!("invalid source `{}`", string))?; match kind { "git" => { @@ -169,7 +169,7 @@ impl SourceId { let url = url.to_url()?; Ok(SourceId::new(Kind::Path, url)) } - kind => Err(human(format!("unsupported source protocol: {}", kind))) + kind => Err(format!("unsupported source protocol: {}", kind).into()) } } diff --git a/src/cargo/core/workspace.rs b/src/cargo/core/workspace.rs index 6486233a8d0..36df965eb2f 100644 --- a/src/cargo/core/workspace.rs +++ b/src/cargo/core/workspace.rs @@ -8,7 +8,8 @@ use glob::glob; use core::{Package, VirtualManifest, EitherManifest, SourceId}; use core::{PackageIdSpec, Dependency, Profile, Profiles}; use ops; -use util::{Config, CargoResult, Filesystem, human, ChainError}; +use util::{Config, Filesystem}; +use util::errors::{CargoResult, CargoResultExt}; use util::paths; /// The core abstraction in Cargo for working with a workspace of crates. @@ -164,9 +165,9 @@ impl<'cfg> Workspace<'cfg> { /// indicating that something else should be passed. pub fn current(&self) -> CargoResult<&Package> { self.current_opt().ok_or_else(|| - human(format!("manifest path `{}` is a virtual manifest, but this \ - command requires running against an actual package in \ - this workspace", self.current_manifest.display())) + format!("manifest path `{}` is a virtual manifest, but this \ + command requires running against an actual package in \ + this workspace", self.current_manifest.display()).into() ) } @@ -551,12 +552,12 @@ fn expand_member_path(path: &Path) -> CargoResult> { Some(p) => p, None => return Ok(Vec::new()), }; - let res = glob(path).chain_error(|| { - human(format!("could not parse pattern `{}`", &path)) + let res = glob(path).chain_err(|| { + format!("could not parse pattern `{}`", &path) })?; res.map(|p| { - p.chain_error(|| { - human(format!("unable to match path to pattern `{}`", &path)) + p.chain_err(|| { + format!("unable to match path to pattern `{}`", &path) }) }).collect() } diff --git a/src/cargo/lib.rs b/src/cargo/lib.rs index 8403b9a923c..a03662927b9 100755 --- a/src/cargo/lib.rs +++ b/src/cargo/lib.rs @@ -1,7 +1,9 @@ #![deny(unused)] #![cfg_attr(test, deny(warnings))] +#![recursion_limit="128"] #[cfg(test)] extern crate hamcrest; +#[macro_use] extern crate error_chain; #[macro_use] extern crate log; #[macro_use] extern crate serde_derive; #[macro_use] extern crate serde_json; @@ -30,6 +32,9 @@ extern crate url; use std::io; use std::fmt; +use std::error::Error; + +use error_chain::ChainedError; use rustc_serialize::Decodable; use serde::ser; use docopt::Docopt; @@ -38,13 +43,13 @@ use core::{Shell, MultiShell, ShellConfig, Verbosity, ColorConfig}; use core::shell::Verbosity::{Verbose}; use term::color::{BLACK}; -pub use util::{CargoError, CargoResult, CliError, CliResult, human, Config, ChainError}; +pub use util::{CargoError, CargoErrorKind, CargoResult, CliError, CliResult, Config}; pub const CARGO_ENV: &'static str = "CARGO"; macro_rules! bail { ($($fmt:tt)*) => ( - return Err(::util::human(&format_args!($($fmt)*))) + return Err(::util::errors::CargoError::from(format_args!($($fmt)*).to_string())) ) } @@ -112,7 +117,7 @@ pub fn call_main_without_stdin( let flags = docopt.decode().map_err(|e| { let code = if e.fatal() {1} else {0}; - CliError::new(human(e.to_string()), code) + CliError::new(e.to_string().into(), code) })?; exec(flags, config) @@ -186,7 +191,7 @@ pub fn exit_with_error(err: CliError, shell: &mut MultiShell) -> ! { shell.say(&error, BLACK) }; - if !handle_cause(&error, shell) || hide { + if !handle_cause(error, shell) || hide { let _ = shell.err().say("\nTo learn more, run the command again \ with --verbose.".to_string(), BLACK); } @@ -195,37 +200,58 @@ pub fn exit_with_error(err: CliError, shell: &mut MultiShell) -> ! { std::process::exit(exit_code) } -pub fn handle_error(err: &CargoError, shell: &mut MultiShell) { - debug!("handle_error; err={:?}", err); +pub fn handle_error(err: CargoError, shell: &mut MultiShell) { + debug!("handle_error; err={:?}", &err); - let _ignored_result = shell.error(err); + let _ignored_result = shell.error(&err); handle_cause(err, shell); } -fn handle_cause(mut cargo_err: &CargoError, shell: &mut MultiShell) -> bool { - let verbose = shell.get_verbose(); - let mut err; - loop { - cargo_err = match cargo_err.cargo_cause() { - Some(cause) => cause, - None => { err = cargo_err.cause(); break } - }; - if verbose != Verbose && !cargo_err.is_human() { return false } - print(cargo_err.to_string(), shell); - } - loop { - let cause = match err { Some(err) => err, None => return true }; - if verbose != Verbose { return false } - print(cause.to_string(), shell); - err = cause.cause(); - } - +fn handle_cause(cargo_err: E, shell: &mut MultiShell) -> bool + where E: ChainedError + 'static { fn print(error: String, shell: &mut MultiShell) { let _ = shell.err().say("\nCaused by:", BLACK); let _ = shell.err().say(format!(" {}", error), BLACK); } + + //Error inspection in non-verbose mode requires inspecting the + //error kind to avoid printing Internal errors. The downcasting + //machinery requires &(Error + 'static), but the iterator (and + //underlying `cause`) return &Error. Because the borrows are + //constrained to this handling method, and because the original + //error object is constrained to be 'static, we're casting away + //the borrow's actual lifetime for purposes of downcasting and + //inspecting the error chain + unsafe fn extend_lifetime(r: &Error) -> &(Error + 'static) { + std::mem::transmute::<&Error, &Error>(r) + } + + let verbose = shell.get_verbose(); + + if verbose == Verbose { + //The first error has already been printed to the shell + //Print all remaining errors + for err in cargo_err.iter().skip(1) { + print(err.to_string(), shell); + } + } else { + //The first error has already been printed to the shell + //Print remaining errors until one marked as Internal appears + for err in cargo_err.iter().skip(1) { + let err = unsafe { extend_lifetime(err) }; + if let Some(&CargoError(CargoErrorKind::Internal(..), ..)) = + err.downcast_ref::() { + return false; + } + + print(err.to_string(), shell); + } + } + + true } + pub fn version() -> VersionInfo { macro_rules! env_str { ($name:expr) => { env!($name).to_string() } diff --git a/src/cargo/ops/cargo_clean.rs b/src/cargo/ops/cargo_clean.rs index b7c214a75d9..c7f1b28f8b6 100644 --- a/src/cargo/ops/cargo_clean.rs +++ b/src/cargo/ops/cargo_clean.rs @@ -3,7 +3,8 @@ use std::fs; use std::path::Path; use core::{Profiles, Workspace}; -use util::{CargoResult, human, ChainError, Config}; +use util::Config; +use util::errors::{CargoResult, CargoResultExt}; use ops::{self, Context, BuildConfig, Kind, Unit}; pub struct CleanOptions<'a> { @@ -95,12 +96,12 @@ pub fn clean(ws: &Workspace, opts: &CleanOptions) -> CargoResult<()> { fn rm_rf(path: &Path) -> CargoResult<()> { let m = fs::metadata(path); if m.as_ref().map(|s| s.is_dir()).unwrap_or(false) { - fs::remove_dir_all(path).chain_error(|| { - human("could not remove build directory") + fs::remove_dir_all(path).chain_err(|| { + "could not remove build directory" })?; } else if m.is_ok() { - fs::remove_file(path).chain_error(|| { - human("failed to remove build artifact") + fs::remove_file(path).chain_err(|| { + "failed to remove build artifact" })?; } Ok(()) diff --git a/src/cargo/ops/cargo_install.rs b/src/cargo/ops/cargo_install.rs index d19ac402faa..ba1829accbd 100644 --- a/src/cargo/ops/cargo_install.rs +++ b/src/cargo/ops/cargo_install.rs @@ -16,8 +16,9 @@ use core::{SourceId, Source, Package, Dependency, PackageIdSpec}; use core::{PackageId, Workspace}; use ops::{self, CompileFilter, DefaultExecutor}; use sources::{GitSource, PathSource, SourceConfigMap}; -use util::{CargoResult, ChainError, Config, human, internal}; +use util::{Config, internal}; use util::{Filesystem, FileLock}; +use util::errors::{CargoError, CargoResult, CargoResultExt}; #[derive(Deserialize, Serialize)] #[serde(untagged)] @@ -69,19 +70,19 @@ pub fn install(root: Option<&str>, let path = source_id.url().to_file_path().ok() .expect("path sources must have a valid path"); let mut src = PathSource::new(&path, source_id, config); - src.update().chain_error(|| { - human(format!("`{}` is not a crate root; specify a crate to \ - install from crates.io, or use --path or --git to \ - specify an alternate source", path.display())) + src.update().chain_err(|| { + format!("`{}` is not a crate root; specify a crate to \ + install from crates.io, or use --path or --git to \ + specify an alternate source", path.display()) })?; select_pkg(PathSource::new(&path, source_id, config), krate, vers, config, &mut |path| path.read_packages())? } else { select_pkg(map.load(source_id)?, krate, vers, config, - &mut |_| Err(human("must specify a crate to install from \ - crates.io, or use --path or --git to \ - specify alternate source")))? + &mut |_| Err("must specify a crate to install from \ + crates.io, or use --path or --git to \ + specify alternate source".into()))? }; @@ -117,14 +118,14 @@ pub fn install(root: Option<&str>, let compile = ops::compile_ws(&ws, Some(source), opts, - Arc::new(DefaultExecutor)).chain_error(|| { + Arc::new(DefaultExecutor)).chain_err(|| { if let Some(td) = td_opt.take() { // preserve the temporary directory, so the user can inspect it td.into_path(); } - human(format!("failed to compile `{}`, intermediate artifacts can be \ - found at `{}`", pkg, ws.target_dir().display())) + CargoError::from(format!("failed to compile `{}`, intermediate artifacts can be \ + found at `{}`", pkg, ws.target_dir().display())) })?; let binaries: Vec<(&str, &Path)> = compile.binaries.iter().map(|bin| { let name = bin.file_name().unwrap(); @@ -159,9 +160,9 @@ pub fn install(root: Option<&str>, continue } } - fs::copy(src, &dst).chain_error(|| { - human(format!("failed to copy `{}` to `{}`", src.display(), - dst.display())) + fs::copy(src, &dst).chain_err(|| { + format!("failed to copy `{}` to `{}`", src.display(), + dst.display()) })?; } @@ -176,9 +177,9 @@ pub fn install(root: Option<&str>, let src = staging_dir.path().join(bin); let dst = dst.join(bin); config.shell().status("Installing", dst.display())?; - fs::rename(&src, &dst).chain_error(|| { - human(format!("failed to move `{}` to `{}`", src.display(), - dst.display())) + fs::rename(&src, &dst).chain_err(|| { + format!("failed to move `{}` to `{}`", src.display(), + dst.display()) })?; installed.bins.push(dst); } @@ -192,9 +193,9 @@ pub fn install(root: Option<&str>, let src = staging_dir.path().join(bin); let dst = dst.join(bin); config.shell().status("Replacing", dst.display())?; - fs::rename(&src, &dst).chain_error(|| { - human(format!("failed to move `{}` to `{}`", src.display(), - dst.display())) + fs::rename(&src, &dst).chain_err(|| { + format!("failed to move `{}` to `{}`", src.display(), + dst.display()) })?; replaced_names.push(bin); } @@ -234,7 +235,7 @@ pub fn install(root: Option<&str>, match write_result { // Replacement error (if any) isn't actually caused by write error // but this seems to be the only way to show both. - Err(err) => result.chain_error(|| err)?, + Err(err) => result.chain_err(|| err)?, Ok(_) => result?, } @@ -303,8 +304,8 @@ fn select_pkg<'a, T>(mut source: T, None => { let vers_info = vers.map(|v| format!(" with version `{}`", v)) .unwrap_or(String::new()); - Err(human(format!("could not find `{}` in `{}`{}", name, - source.source_id(), vers_info))) + Err(format!("could not find `{}` in `{}`{}", name, + source.source_id(), vers_info).into()) } } } @@ -346,7 +347,7 @@ fn one(mut i: I, f: F) -> CargoResult> (Some(i1), Some(i2)) => { let mut v = vec![i1, i2]; v.extend(i); - Err(human(f(v))) + Err(f(v).into()) } (Some(i), None) => Ok(Some(i)), (None, _) => Ok(None) @@ -381,7 +382,7 @@ fn check_overwrites(dst: &Path, } } msg.push_str("Add --force to overwrite"); - Err(human(msg)) + Err(msg.into()) } fn find_duplicates(dst: &Path, @@ -429,7 +430,7 @@ fn read_crate_list(mut file: &File) -> CargoResult { (|| -> CargoResult<_> { let mut contents = String::new(); file.read_to_string(&mut contents)?; - let listing = toml::from_str(&contents).chain_error(|| { + let listing = toml::from_str(&contents).chain_err(|| { internal("invalid TOML found for metadata") })?; match listing { @@ -438,8 +439,8 @@ fn read_crate_list(mut file: &File) -> CargoResult { Ok(CrateListingV1 { v1: BTreeMap::new() }) } } - }).chain_error(|| { - human("failed to parse crate metadata") + })().chain_err(|| { + "failed to parse crate metadata" }) } @@ -450,8 +451,8 @@ fn write_crate_list(mut file: &File, listing: CrateListingV1) -> CargoResult<()> let data = toml::to_string(&CrateListing::V1(listing))?; file.write_all(data.as_bytes())?; Ok(()) - }).chain_error(|| { - human("failed to write crate metadata") + })().chain_err(|| { + "failed to write crate metadata" }) } diff --git a/src/cargo/ops/cargo_new.rs b/src/cargo/ops/cargo_new.rs index 9e08eee13c7..1d1f1bc7c32 100644 --- a/src/cargo/ops/cargo_new.rs +++ b/src/cargo/ops/cargo_new.rs @@ -11,8 +11,9 @@ use term::color::BLACK; use core::Workspace; use ops::is_bad_artifact_name; -use util::{GitRepo, HgRepo, PijulRepo, CargoResult, human, ChainError, internal}; +use util::{GitRepo, HgRepo, PijulRepo, internal}; use util::{Config, paths}; +use util::errors::{CargoError, CargoResult, CargoResultExt}; use toml; @@ -97,9 +98,9 @@ fn get_name<'a>(path: &'a Path, opts: &'a NewOptions, config: &Config) -> CargoR path.as_os_str()); } - let dir_name = path.file_name().and_then(|s| s.to_str()).chain_error(|| { - human(&format!("cannot create a project with a non-unicode name: {:?}", - path.file_name().unwrap())) + let dir_name = path.file_name().and_then(|s| s.to_str()).ok_or_else(|| { + CargoError::from(format!("cannot create a project with a non-unicode name: {:?}", + path.file_name().unwrap())) })?; if opts.bin { @@ -235,10 +236,10 @@ cannot automatically generate Cargo.toml as the main target would be ambiguous", duplicates_checker.insert(i.target_name.as_ref(), i); } else { if let Some(plp) = previous_lib_relpath { - return Err(human(format!("cannot have a project with \ - multiple libraries, \ - found both `{}` and `{}`", - plp, i.relative_path))); + return Err(format!("cannot have a project with \ + multiple libraries, \ + found both `{}` and `{}`", + plp, i.relative_path).into()); } previous_lib_relpath = Some(&i.relative_path); } @@ -285,9 +286,9 @@ pub fn new(opts: NewOptions, config: &Config) -> CargoResult<()> { bin: opts.bin, }; - mk(config, &mkopts).chain_error(|| { - human(format!("Failed to create project `{}` at `{}`", - name, path.display())) + mk(config, &mkopts).chain_err(|| { + format!("Failed to create project `{}` at `{}`", + name, path.display()) }) } @@ -356,9 +357,9 @@ pub fn init(opts: NewOptions, config: &Config) -> CargoResult<()> { source_files: src_paths_types, }; - mk(config, &mkopts).chain_error(|| { - human(format!("Failed to create project `{}` at `{}`", - name, path.display())) + mk(config, &mkopts).chain_err(|| { + format!("Failed to create project `{}` at `{}`", + name, path.display()) }) } diff --git a/src/cargo/ops/cargo_package.rs b/src/cargo/ops/cargo_package.rs index de788bab5b6..c46c7b2eeeb 100644 --- a/src/cargo/ops/cargo_package.rs +++ b/src/cargo/ops/cargo_package.rs @@ -11,7 +11,8 @@ use tar::{Archive, Builder, Header, EntryType}; use core::{Package, Workspace, Source, SourceId}; use sources::PathSource; -use util::{self, CargoResult, human, internal, ChainError, Config, FileLock}; +use util::{self, internal, Config, FileLock}; +use util::errors::{CargoResult, CargoResultExt}; use ops::{self, DefaultExecutor}; pub struct PackageOpts<'cfg> { @@ -67,21 +68,21 @@ pub fn package(ws: &Workspace, // it exists. config.shell().status("Packaging", pkg.package_id().to_string())?; dst.file().set_len(0)?; - tar(ws, &src, dst.file(), &filename).chain_error(|| { - human("failed to prepare local package for uploading") + tar(ws, &src, dst.file(), &filename).chain_err(|| { + "failed to prepare local package for uploading" })?; if opts.verify { dst.seek(SeekFrom::Start(0))?; - run_verify(ws, dst.file(), opts).chain_error(|| { - human("failed to verify package tarball") + run_verify(ws, dst.file(), opts).chain_err(|| { + "failed to verify package tarball" })? } dst.seek(SeekFrom::Start(0))?; { let src_path = dst.path(); let dst_path = dst.parent().join(&filename); - fs::rename(&src_path, &dst_path).chain_error(|| { - human("failed to move temporary tarball into final location") + fs::rename(&src_path, &dst_path).chain_err(|| { + "failed to move temporary tarball into final location" })?; } Ok(Some(dst)) @@ -198,9 +199,9 @@ fn tar(ws: &Workspace, for file in src.list_files(pkg)?.iter() { let relative = util::without_prefix(&file, &root).unwrap(); check_filename(relative)?; - let relative = relative.to_str().chain_error(|| { - human(format!("non-utf8 path in source directory: {}", - relative.display())) + let relative = relative.to_str().ok_or_else(|| { + format!("non-utf8 path in source directory: {}", + relative.display()) })?; config.shell().verbose(|shell| { shell.status("Archiving", &relative) @@ -227,14 +228,14 @@ fn tar(ws: &Workspace, // unpack the selectors 0.4.0 crate on crates.io. Either that or take a // look at rust-lang/cargo#2326 let mut header = Header::new_ustar(); - header.set_path(&path).chain_error(|| { - human(format!("failed to add to archive: `{}`", relative)) + header.set_path(&path).chain_err(|| { + format!("failed to add to archive: `{}`", relative) })?; - let mut file = File::open(file).chain_error(|| { - human(format!("failed to open for archiving: `{}`", file.display())) + let mut file = File::open(file).chain_err(|| { + format!("failed to open for archiving: `{}`", file.display()) })?; - let metadata = file.metadata().chain_error(|| { - human(format!("could not learn metadata for: `{}`", relative)) + let metadata = file.metadata().chain_err(|| { + format!("could not learn metadata for: `{}`", relative) })?; header.set_metadata(&metadata); @@ -242,7 +243,7 @@ fn tar(ws: &Workspace, let orig = Path::new(&path).with_file_name("Cargo.toml.orig"); header.set_path(&orig)?; header.set_cksum(); - ar.append(&header, &mut file).chain_error(|| { + ar.append(&header, &mut file).chain_err(|| { internal(format!("could not archive source file `{}`", relative)) })?; @@ -253,12 +254,12 @@ fn tar(ws: &Workspace, header.set_mode(0o644); header.set_size(toml.len() as u64); header.set_cksum(); - ar.append(&header, toml.as_bytes()).chain_error(|| { + ar.append(&header, toml.as_bytes()).chain_err(|| { internal(format!("could not archive source file `{}`", relative)) })?; } else { header.set_cksum(); - ar.append(&header, &mut file).chain_error(|| { + ar.append(&header, &mut file).chain_err(|| { internal(format!("could not archive source file `{}`", relative)) })?; } diff --git a/src/cargo/ops/cargo_read_manifest.rs b/src/cargo/ops/cargo_read_manifest.rs index 09f8dec855a..98cfde1d389 100644 --- a/src/cargo/ops/cargo_read_manifest.rs +++ b/src/cargo/ops/cargo_read_manifest.rs @@ -4,7 +4,8 @@ use std::io; use std::path::{Path, PathBuf}; use core::{Package, SourceId, PackageId, EitherManifest}; -use util::{self, paths, CargoResult, human, Config, ChainError}; +use util::{self, paths, Config}; +use util::errors::{CargoResult, CargoResultExt}; use util::important_paths::find_project_manifest_exact; use util::toml::Layout; @@ -15,9 +16,9 @@ pub fn read_manifest(path: &Path, source_id: &SourceId, config: &Config) let layout = Layout::from_project_path(path.parent().unwrap()); let root = layout.root.clone(); - util::toml::to_manifest(&contents, source_id, layout, config).chain_error(|| { - human(format!("failed to parse manifest at `{}`", - root.join("Cargo.toml").display())) + util::toml::to_manifest(&contents, source_id, layout, config).chain_err(|| { + format!("failed to parse manifest at `{}`", + root.join("Cargo.toml").display()) }) } @@ -73,7 +74,7 @@ pub fn read_packages(path: &Path, source_id: &SourceId, config: &Config) })?; if all_packages.is_empty() { - Err(human(format!("Could not find Cargo.toml in `{}`", path.display()))) + Err(format!("Could not find Cargo.toml in `{}`", path.display()).into()) } else { Ok(all_packages.into_iter().map(|(_, v)| v).collect()) } @@ -94,8 +95,8 @@ fn walk(path: &Path, callback: &mut FnMut(&Path) -> CargoResult) return Ok(()) } Err(e) => { - return Err(human(e)).chain_error(|| { - human(format!("failed to read directory `{}`", path.display())) + return Err(e).chain_err(|| { + format!("failed to read directory `{}`", path.display()) }) } }; diff --git a/src/cargo/ops/cargo_run.rs b/src/cargo/ops/cargo_run.rs index e631e503dc1..ab769857754 100644 --- a/src/cargo/ops/cargo_run.rs +++ b/src/cargo/ops/cargo_run.rs @@ -1,7 +1,8 @@ use std::path::Path; use ops::{self, Packages}; -use util::{self, human, CargoResult, ProcessError}; +use util::{self, CargoResult, CargoError, ProcessError}; +use util::errors::CargoErrorKind; use core::Workspace; pub fn run(ws: &Workspace, @@ -16,9 +17,10 @@ pub fn run(ws: &Workspace, 0 => ws.current()?, 1 => ws.members() .find(|pkg| pkg.name() == xs[0]) - .ok_or_else(|| human( - format!("package `{}` is not a member of the workspace", xs[0]) - ))?, + .ok_or_else(|| + CargoError::from( + format!("package `{}` is not a member of the workspace", xs[0])) + )?, _ => unreachable!("cargo run supports single package only"), } }; @@ -61,5 +63,12 @@ pub fn run(ws: &Workspace, process.args(args).cwd(config.cwd()); config.shell().status("Running", process.to_string())?; - Ok(process.exec_replace().err()) + + let result = process.exec_replace(); + + match result { + Ok(()) => Ok(None), + Err(CargoError(CargoErrorKind::ProcessErrorKind(e), ..)) => Ok(Some(e)), + Err(e) => Err(e) + } } diff --git a/src/cargo/ops/cargo_rustc/context.rs b/src/cargo/ops/cargo_rustc/context.rs index 77131455ae6..f78e67049a7 100644 --- a/src/cargo/ops/cargo_rustc/context.rs +++ b/src/cargo/ops/cargo_rustc/context.rs @@ -11,7 +11,8 @@ use std::sync::Arc; use core::{Package, PackageId, PackageSet, Resolve, Target, Profile}; use core::{TargetKind, Profiles, Dependency, Workspace}; use core::dependency::Kind as DepKind; -use util::{self, CargoResult, ChainError, internal, Config, profile, Cfg, CfgExpr, human}; +use util::{self, internal, Config, profile, Cfg, CfgExpr}; +use util::errors::{CargoResult, CargoResultExt}; use super::TargetConfig; use super::custom_build::{BuildState, BuildScripts}; @@ -121,12 +122,12 @@ impl<'a, 'cfg> Context<'a, 'cfg> { pub fn prepare(&mut self) -> CargoResult<()> { let _p = profile::start("preparing layout"); - self.host.prepare().chain_error(|| { + self.host.prepare().chain_err(|| { internal(format!("couldn't prepare build directories")) })?; match self.target { Some(ref mut target) => { - target.prepare().chain_error(|| { + target.prepare().chain_err(|| { internal(format!("couldn't prepare build directories")) })?; } @@ -213,9 +214,9 @@ impl<'a, 'cfg> Context<'a, 'cfg> { let output = with_cfg.exec_with_output().or_else(|_| { has_cfg_and_sysroot = false; process.exec_with_output() - }).chain_error(|| { - human(format!("failed to run `rustc` to learn about \ - target-specific information")) + }).chain_err(|| { + format!("failed to run `rustc` to learn about \ + target-specific information") })?; let error = str::from_utf8(&output.stderr).unwrap(); diff --git a/src/cargo/ops/cargo_rustc/custom_build.rs b/src/cargo/ops/cargo_rustc/custom_build.rs index 4514ab42bbb..3e988865a2a 100644 --- a/src/cargo/ops/cargo_rustc/custom_build.rs +++ b/src/cargo/ops/cargo_rustc/custom_build.rs @@ -5,8 +5,9 @@ use std::str; use std::sync::{Mutex, Arc}; use core::PackageId; -use util::{CargoResult, Human, Freshness, Cfg}; -use util::{internal, ChainError, profile, paths}; +use util::{Freshness, Cfg}; +use util::errors::{CargoResult, CargoResultExt, CargoError}; +use util::{internal, profile, paths}; use util::machine_message; use super::job::Work; @@ -201,7 +202,7 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) // If we have an old build directory, then just move it into place, // otherwise create it! if fs::metadata(&build_output).is_err() { - fs::create_dir(&build_output).chain_error(|| { + fs::create_dir(&build_output).chain_err(|| { internal("failed to create script output directory for \ build command") })?; @@ -215,7 +216,7 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) let build_state = build_state.outputs.lock().unwrap(); for (name, id) in lib_deps { let key = (id.clone(), kind); - let state = build_state.get(&key).chain_error(|| { + let state = build_state.get(&key).ok_or_else(|| { internal(format!("failed to locate build state for env \ vars: {}/{:?}", id, kind)) })?; @@ -237,10 +238,11 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) let output = cmd.exec_with_streaming( &mut |out_line| { state.stdout(out_line); Ok(()) }, &mut |err_line| { state.stderr(err_line); Ok(()) }, - ).map_err(|mut e| { - e.desc = format!("failed to run custom build command for `{}`\n{}", - pkg_name, e.desc); - Human(e) + ).map_err(|e| { + CargoError::from( + format!("failed to run custom build command for `{}`\n{}", + pkg_name, e.description())) + })?; paths::write(&output_file, &output.stdout)?; diff --git a/src/cargo/ops/cargo_rustc/fingerprint.rs b/src/cargo/ops/cargo_rustc/fingerprint.rs index dbae58eec0d..816bbe154b9 100644 --- a/src/cargo/ops/cargo_rustc/fingerprint.rs +++ b/src/cargo/ops/cargo_rustc/fingerprint.rs @@ -12,7 +12,8 @@ use serde_json; use core::{Package, TargetKind}; use util; -use util::{CargoResult, Fresh, Dirty, Freshness, internal, profile, ChainError}; +use util::{Fresh, Dirty, Freshness, internal, profile}; +use util::errors::{CargoResult, CargoResultExt}; use util::paths; use super::job::Work; @@ -72,7 +73,7 @@ pub fn prepare_target<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, if compare.is_err() { let source_id = unit.pkg.package_id().source_id(); let sources = cx.packages.sources(); - let source = sources.get(source_id).chain_error(|| { + let source = sources.get(source_id).ok_or_else(|| { internal("missing package source") })?; source.verify(unit.pkg.package_id())?; @@ -180,9 +181,10 @@ impl Fingerprint { fn update_local(&self) -> CargoResult<()> { match self.local { LocalFingerprint::MtimeBased(ref slot, ref path) => { - let meta = fs::metadata(path).chain_error(|| { - internal(format!("failed to stat `{}`", path.display())) - })?; + let meta = fs::metadata(path) + .chain_err(|| { + internal(format!("failed to stat `{}`", path.display())) + })?; let mtime = FileTime::from_last_modification_time(&meta); *slot.0.lock().unwrap() = Some(mtime); } @@ -506,26 +508,20 @@ fn compare_old_fingerprint(loc: &Path, new_fingerprint: &Fingerprint) } let old_fingerprint_json = paths::read(&loc.with_extension("json"))?; - let old_fingerprint = serde_json::from_str(&old_fingerprint_json).chain_error(|| { - internal(format!("failed to deserialize json")) - })?; + let old_fingerprint = serde_json::from_str(&old_fingerprint_json) + .chain_err(|| internal(format!("failed to deserialize json")))?; new_fingerprint.compare(&old_fingerprint) } fn log_compare(unit: &Unit, compare: &CargoResult<()>) { - let mut e = match *compare { + let ce = match *compare { Ok(..) => return, - Err(ref e) => &**e, + Err(ref e) => e, }; - info!("fingerprint error for {}: {}", unit.pkg, e); - while let Some(cause) = e.cargo_cause() { - info!(" cause: {}", cause); - e = cause; - } - let mut e = e.cause(); - while let Some(cause) = e { + info!("fingerprint error for {}: {}", unit.pkg, ce); + + for cause in ce.iter() { info!(" cause: {}", cause); - e = cause.cause(); } } @@ -545,7 +541,7 @@ pub fn parse_dep_info(dep_info: &Path) -> CargoResult>> { Some(Ok(line)) => line, _ => return Ok(None), }; - let pos = line.find(": ").chain_error(|| { + let pos = line.find(": ").ok_or_else(|| { internal(format!("dep-info not in an understood format: {}", dep_info.display())) })?; @@ -558,7 +554,7 @@ pub fn parse_dep_info(dep_info: &Path) -> CargoResult>> { while file.ends_with('\\') { file.pop(); file.push(' '); - file.push_str(deps.next().chain_error(|| { + file.push_str(deps.next().ok_or_else(|| { internal("malformed dep-info format, trailing \\".to_string()) })?); } @@ -578,7 +574,8 @@ fn dep_info_mtime_if_fresh(dep_info: &Path) -> CargoResult> { fn pkg_fingerprint(cx: &Context, pkg: &Package) -> CargoResult { let source_id = pkg.package_id().source_id(); let sources = cx.packages.sources(); - let source = sources.get(source_id).chain_error(|| { + + let source = sources.get(source_id).ok_or_else(|| { internal("missing package source") })?; source.fingerprint(pkg) diff --git a/src/cargo/ops/cargo_rustc/job_queue.rs b/src/cargo/ops/cargo_rustc/job_queue.rs index 344c81b8075..bfa374780d3 100644 --- a/src/cargo/ops/cargo_rustc/job_queue.rs +++ b/src/cargo/ops/cargo_rustc/job_queue.rs @@ -9,7 +9,7 @@ use term::color::YELLOW; use core::{PackageId, Target, Profile}; use util::{Config, DependencyQueue, Fresh, Dirty, Freshness}; -use util::{CargoResult, ProcessBuilder, profile, internal, human}; +use util::{CargoResult, ProcessBuilder, profile, internal}; use {handle_error}; use super::{Context, Kind, Unit}; @@ -186,14 +186,13 @@ impl<'a> JobQueue<'a> { self.emit_warnings(Some(msg), key, cx)?; if self.active > 0 { - error = Some(human("build failed")); - handle_error(&*e, &mut *cx.config.shell()); + error = Some("build failed".into()); + handle_error(e, &mut *cx.config.shell()); cx.config.shell().say( "Build failed, waiting for other \ jobs to finish...", YELLOW)?; } - - if error.is_none() { + else { error = Some(e); } } diff --git a/src/cargo/ops/cargo_rustc/layout.rs b/src/cargo/ops/cargo_rustc/layout.rs index 66882e628a2..83c66e85a07 100644 --- a/src/cargo/ops/cargo_rustc/layout.rs +++ b/src/cargo/ops/cargo_rustc/layout.rs @@ -54,7 +54,7 @@ use std::io; use std::path::{PathBuf, Path}; use core::Workspace; -use util::{Config, FileLock, CargoResult, Filesystem, human}; +use util::{Config, FileLock, CargoResult, Filesystem}; pub struct Layout { root: PathBuf, @@ -83,7 +83,7 @@ impl Layout { // the target triple as a Path and then just use the file stem as the // component for the directory name. if let Some(triple) = triple { - path.push(Path::new(triple).file_stem().ok_or(human("target was empty".to_string()))?); + path.push(Path::new(triple).file_stem().ok_or_else(|| "target was empty")?); } path.push(dest); Layout::at(ws.config(), path) diff --git a/src/cargo/ops/cargo_rustc/mod.rs b/src/cargo/ops/cargo_rustc/mod.rs index c827887948f..c2dd2a80656 100644 --- a/src/cargo/ops/cargo_rustc/mod.rs +++ b/src/cargo/ops/cargo_rustc/mod.rs @@ -11,8 +11,9 @@ use serde_json; use core::{Package, PackageId, PackageSet, Target, Resolve}; use core::{Profile, Profiles, Workspace}; use core::shell::ColorConfig; -use util::{self, CargoResult, ProcessBuilder, ProcessError, human, machine_message}; -use util::{Config, internal, ChainError, profile, join_paths, short_hash}; +use util::{self, ProcessBuilder, machine_message}; +use util::{Config, internal, profile, join_paths, short_hash}; +use util::errors::{CargoResult, CargoResultExt}; use util::Freshness; use self::job::{Job, Work}; @@ -67,7 +68,7 @@ pub trait Executor: Send + Sync + 'static { fn init(&self, _cx: &Context) {} /// If execution succeeds, the ContinueBuild value indicates whether Cargo /// should continue with the build process for this package. - fn exec(&self, cmd: ProcessBuilder, _id: &PackageId) -> Result<(), ProcessError> { + fn exec(&self, cmd: ProcessBuilder, _id: &PackageId) -> CargoResult<()> { cmd.exec()?; Ok(()) } @@ -77,7 +78,7 @@ pub trait Executor: Send + Sync + 'static { _id: &PackageId, handle_stdout: &mut FnMut(&str) -> CargoResult<()>, handle_stderr: &mut FnMut(&str) -> CargoResult<()>) - -> Result<(), ProcessError> { + -> CargoResult<()> { cmd.exec_with_streaming(handle_stdout, handle_stderr)?; Ok(()) } @@ -333,8 +334,8 @@ fn rustc(cx: &mut Context, unit: &Unit, exec: Arc) -> CargoResult) -> CargoResult) -> CargoResult) -> CargoResult) -> CargoResult CargoResult<()> { for key in build_scripts.to_link.iter() { - let output = build_state.get(key).chain_error(|| { + let output = build_state.get(key).ok_or_else(|| { internal(format!("couldn't find build state for {}/{:?}", key.0, key.1)) })?; @@ -480,8 +481,8 @@ fn link_targets(cx: &mut Context, unit: &Unit, fresh: bool) -> CargoResult debug!("linking {} to {}", src.display(), dst.display()); if dst.exists() { - fs::remove_file(&dst).chain_error(|| { - human(format!("failed to remove: {}", dst.display())) + fs::remove_file(&dst).chain_err(|| { + format!("failed to remove: {}", dst.display()) })?; } fs::hard_link(src, dst) @@ -489,9 +490,9 @@ fn link_targets(cx: &mut Context, unit: &Unit, fresh: bool) -> CargoResult debug!("hard link failed {}. falling back to fs::copy", err); fs::copy(src, dst).map(|_| ()) }) - .chain_error(|| { - human(format!("failed to link or copy `{}` to `{}`", - src.display(), dst.display())) + .chain_err(|| { + format!("failed to link or copy `{}` to `{}`", + src.display(), dst.display()) })?; } @@ -526,7 +527,7 @@ fn add_plugin_deps(rustc: &mut ProcessBuilder, let mut search_path = env::split_paths(&search_path).collect::>(); for id in build_scripts.plugins.iter() { let key = (id.clone(), Kind::Host); - let output = build_state.get(&key).chain_error(|| { + let output = build_state.get(&key).ok_or_else(|| { internal(format!("couldn't find libs for plugin dep {}", id)) })?; search_path.append(&mut filter_dynamic_search_path(output.library_paths.iter(), @@ -627,9 +628,7 @@ fn rustdoc(cx: &mut Context, unit: &Unit) -> CargoResult { } } state.running(&rustdoc); - rustdoc.exec().chain_error(|| { - human(format!("Could not document `{}`.", name)) - }) + rustdoc.exec().chain_err(|| format!("Could not document `{}`.", name)) })) } diff --git a/src/cargo/ops/cargo_test.rs b/src/cargo/ops/cargo_test.rs index b659965d5b7..86e43173302 100644 --- a/src/cargo/ops/cargo_test.rs +++ b/src/cargo/ops/cargo_test.rs @@ -1,7 +1,8 @@ use std::ffi::{OsString, OsStr}; use ops::{self, Compilation}; -use util::{self, CargoResult, CargoTestError, Test, ProcessError}; +use util::{self, CargoTestError, Test, ProcessError}; +use util::errors::{CargoResult, CargoErrorKind, CargoError}; use core::Workspace; pub struct TestOptions<'a> { @@ -100,11 +101,20 @@ fn run_unit_tests(options: &TestOptions, shell.status("Running", cmd.to_string()) })?; - if let Err(e) = cmd.exec() { - errors.push(e); - if !options.no_fail_fast { - return Ok((Test::UnitTest(kind.clone(), test.clone()), errors)) + let result = cmd.exec(); + + match result { + Err(CargoError(CargoErrorKind::ProcessErrorKind(e), .. )) => { + errors.push(e); + if !options.no_fail_fast { + return Ok((Test::UnitTest(kind.clone(), test.clone()), errors)) + } + } + Err(e) => { + //This is an unexpected Cargo error rather than a test failure + return Err(e) } + Ok(()) => {} } } Ok((Test::Multiple, errors)) @@ -178,7 +188,7 @@ fn run_doc_tests(options: &TestOptions, config.shell().verbose(|shell| { shell.status("Running", p.to_string()) })?; - if let Err(e) = p.exec() { + if let Err(CargoError(CargoErrorKind::ProcessErrorKind(e), .. )) = p.exec() { errors.push(e); if !options.no_fail_fast { return Ok((Test::Doc, errors)); diff --git a/src/cargo/ops/lockfile.rs b/src/cargo/ops/lockfile.rs index 1271cf82d5d..1138be745e1 100644 --- a/src/cargo/ops/lockfile.rs +++ b/src/cargo/ops/lockfile.rs @@ -4,7 +4,8 @@ use toml; use core::{Resolve, resolver, Workspace}; use core::resolver::WorkspaceResolve; -use util::{CargoResult, ChainError, human, Filesystem}; +use util::Filesystem; +use util::errors::{CargoResult, CargoResultExt}; use util::toml as cargo_toml; pub fn load_pkg_lockfile(ws: &Workspace) -> CargoResult> { @@ -16,16 +17,16 @@ pub fn load_pkg_lockfile(ws: &Workspace) -> CargoResult> { let mut f = root.open_ro("Cargo.lock", ws.config(), "Cargo.lock file")?; let mut s = String::new(); - f.read_to_string(&mut s).chain_error(|| { - human(format!("failed to read file: {}", f.path().display())) + f.read_to_string(&mut s).chain_err(|| { + format!("failed to read file: {}", f.path().display()) })?; - (|| { - let resolve = cargo_toml::parse(&s, f.path(), ws.config())?; + (|| -> CargoResult> { + let resolve : toml::Value = cargo_toml::parse(&s, f.path(), ws.config())?; let v: resolver::EncodableResolve = resolve.try_into()?; Ok(Some(v.into_resolve(ws)?)) - }).chain_error(|| { - human(format!("failed to parse lock file at: {}", f.path().display())) + })().chain_err(|| { + format!("failed to parse lock file at: {}", f.path().display()) }) } @@ -98,9 +99,9 @@ pub fn write_pkg_lockfile(ws: &Workspace, resolve: &Resolve) -> CargoResult<()> f.file().set_len(0)?; f.write_all(out.as_bytes())?; Ok(()) - }).chain_error(|| { - human(format!("failed to write {}", - ws.root().join("Cargo.lock").display())) + }).chain_err(|| { + format!("failed to write {}", + ws.root().join("Cargo.lock").display()) }) } diff --git a/src/cargo/ops/registry.rs b/src/cargo/ops/registry.rs index c2915afcf55..6496715631f 100644 --- a/src/cargo/ops/registry.rs +++ b/src/cargo/ops/registry.rs @@ -21,8 +21,9 @@ use ops; use sources::{RegistrySource}; use util::config; use util::paths; -use util::{CargoResult, human, ChainError, ToUrl}; +use util::ToUrl; use util::config::{Config, ConfigValue, Location}; +use util::errors::{CargoError, CargoResult, CargoResultExt}; use util::important_paths::find_root_manifest_for_wd; pub struct RegistryConfig { @@ -175,7 +176,7 @@ fn transmit(config: &Config, Ok(()) }, - Err(e) => Err(human(e.to_string())), + Err(e) => Err(e.into()), } } @@ -200,8 +201,8 @@ pub fn registry(config: &Config, }; let api_host = { let mut src = RegistrySource::remote(&sid, config); - src.update().chain_error(|| { - human(format!("failed to update {}", sid)) + src.update().chain_err(|| { + format!("failed to update {}", sid) })?; (src.config()?).unwrap().api }; @@ -323,7 +324,7 @@ pub fn modify_owners(config: &Config, opts: &OwnersOptions) -> CargoResult<()> { config.shell().status("Owner", format!("adding {:?} to crate {}", v, name))?; registry.add_owners(&name, &v).map_err(|e| { - human(format!("failed to add owners to crate {}: {}", name, e)) + CargoError::from(format!("failed to add owners to crate {}: {}", name, e)) })?; } @@ -332,13 +333,13 @@ pub fn modify_owners(config: &Config, opts: &OwnersOptions) -> CargoResult<()> { config.shell().status("Owner", format!("removing {:?} from crate {}", v, name))?; registry.remove_owners(&name, &v).map_err(|e| { - human(format!("failed to remove owners from crate {}: {}", name, e)) + CargoError::from(format!("failed to remove owners from crate {}: {}", name, e)) })?; } if opts.list { let owners = registry.list_owners(&name).map_err(|e| { - human(format!("failed to list owners of crate {}: {}", name, e)) + CargoError::from(format!("failed to list owners of crate {}: {}", name, e)) })?; for owner in owners.iter() { print!("{}", owner.login); @@ -378,12 +379,12 @@ pub fn yank(config: &Config, if undo { config.shell().status("Unyank", format!("{}:{}", name, version))?; registry.unyank(&name, &version).map_err(|e| { - human(format!("failed to undo a yank: {}", e)) + CargoError::from(format!("failed to undo a yank: {}", e)) })?; } else { config.shell().status("Yank", format!("{}:{}", name, version))?; registry.yank(&name, &version).map_err(|e| { - human(format!("failed to yank: {}", e)) + CargoError::from(format!("failed to yank: {}", e)) })?; } @@ -404,7 +405,7 @@ pub fn search(query: &str, let (mut registry, _) = registry(config, None, index)?; let (crates, total_crates) = registry.search(query, limit).map_err(|e| { - human(format!("failed to retrieve search results from the registry: {}", e)) + CargoError::from(format!("failed to retrieve search results from the registry: {}", e)) })?; let list_items = crates.iter() diff --git a/src/cargo/ops/resolve.rs b/src/cargo/ops/resolve.rs index 2aa60d056dc..8741c74259c 100644 --- a/src/cargo/ops/resolve.rs +++ b/src/cargo/ops/resolve.rs @@ -5,7 +5,8 @@ use core::registry::PackageRegistry; use core::resolver::{self, Resolve, Method}; use sources::PathSource; use ops; -use util::{profile, human, CargoResult, ChainError}; +use util::profile; +use util::errors::{CargoResult, CargoResultExt}; /// Resolve all dependencies for the workspace using the previous /// lockfile as a guide if present. @@ -261,10 +262,10 @@ fn add_overrides<'a>(registry: &mut PackageRegistry<'a>, for (path, definition) in paths { let id = SourceId::for_path(&path)?; let mut source = PathSource::new_recursive(&path, &id, ws.config()); - source.update().chain_error(|| { - human(format!("failed to update path override `{}` \ + source.update().chain_err(|| { + format!("failed to update path override `{}` \ (defined in `{}`)", path.display(), - definition.display())) + definition.display()) })?; registry.add_override(Box::new(source)); } diff --git a/src/cargo/sources/config.rs b/src/cargo/sources/config.rs index 80a13318f65..8799f0c30e8 100644 --- a/src/cargo/sources/config.rs +++ b/src/cargo/sources/config.rs @@ -11,8 +11,9 @@ use url::Url; use core::{Source, SourceId}; use sources::ReplacedSource; -use util::{CargoResult, Config, ChainError, human, ToUrl}; +use util::{Config, ToUrl}; use util::config::ConfigValue; +use util::errors::{CargoError, CargoResult, CargoResultExt}; pub struct SourceConfigMap<'cfg> { cfgs: HashMap, @@ -155,14 +156,14 @@ a lock file compatible with `{orig}` cannot be generated in this situation } let mut srcs = srcs.into_iter(); - let src = srcs.next().chain_error(|| { - human(format!("no source URL specified for `source.{}`, need \ - either `registry` or `local-registry` defined", - name)) + let src = srcs.next().ok_or_else(|| { + CargoError::from(format!("no source URL specified for `source.{}`, need \ + either `registry` or `local-registry` defined", + name)) })?; if srcs.next().is_some() { - return Err(human(format!("more than one source URL specified for \ - `source.{}`", name))) + return Err(format!("more than one source URL specified for \ + `source.{}`", name).into()) } let mut replace_with = None; @@ -181,9 +182,9 @@ a lock file compatible with `{orig}` cannot be generated in this situation fn url(cfg: &ConfigValue, key: &str) -> CargoResult { let (url, path) = cfg.string(key)?; - url.to_url().chain_error(|| { - human(format!("configuration key `{}` specified an invalid \ - URL (in {})", key, path.display())) + url.to_url().chain_err(|| { + format!("configuration key `{}` specified an invalid \ + URL (in {})", key, path.display()) }) } diff --git a/src/cargo/sources/directory.rs b/src/cargo/sources/directory.rs index ee27a13cec3..45a74f1b623 100644 --- a/src/cargo/sources/directory.rs +++ b/src/cargo/sources/directory.rs @@ -9,7 +9,8 @@ use serde_json; use core::{Package, PackageId, Summary, SourceId, Source, Dependency, Registry}; use sources::PathSource; -use util::{CargoResult, human, ChainError, Config, Sha256}; +use util::{Config, Sha256}; +use util::errors::{CargoResult, CargoResultExt}; use util::paths; pub struct DirectorySource<'cfg> { @@ -63,9 +64,9 @@ impl<'cfg> Source for DirectorySource<'cfg> { fn update(&mut self) -> CargoResult<()> { self.packages.clear(); - let entries = self.root.read_dir().chain_error(|| { - human(format!("failed to read root of directory source: {}", - self.root.display())) + let entries = self.root.read_dir().chain_err(|| { + format!("failed to read root of directory source: {}", + self.root.display()) })?; for entry in entries { @@ -112,18 +113,18 @@ impl<'cfg> Source for DirectorySource<'cfg> { let pkg = src.root_package()?; let cksum_file = path.join(".cargo-checksum.json"); - let cksum = paths::read(&path.join(cksum_file)).chain_error(|| { - human(format!("failed to load checksum `.cargo-checksum.json` \ - of {} v{}", - pkg.package_id().name(), - pkg.package_id().version())) + let cksum = paths::read(&path.join(cksum_file)).chain_err(|| { + format!("failed to load checksum `.cargo-checksum.json` \ + of {} v{}", + pkg.package_id().name(), + pkg.package_id().version()) })?; - let cksum: Checksum = serde_json::from_str(&cksum).chain_error(|| { - human(format!("failed to decode `.cargo-checksum.json` of \ - {} v{}", - pkg.package_id().name(), - pkg.package_id().version())) + let cksum: Checksum = serde_json::from_str(&cksum).chain_err(|| { + format!("failed to decode `.cargo-checksum.json` of \ + {} v{}", + pkg.package_id().name(), + pkg.package_id().version()) })?; let mut manifest = pkg.manifest().clone(); @@ -137,8 +138,8 @@ impl<'cfg> Source for DirectorySource<'cfg> { } fn download(&mut self, id: &PackageId) -> CargoResult { - self.packages.get(id).map(|p| &p.0).cloned().chain_error(|| { - human(format!("failed to find package with id: {}", id)) + self.packages.get(id).map(|p| &p.0).cloned().ok_or_else(|| { + format!("failed to find package with id: {}", id).into() }) } @@ -166,9 +167,9 @@ impl<'cfg> Source for DirectorySource<'cfg> { n => h.update(&buf[..n]), } } - }).chain_error(|| { - human(format!("failed to calculate checksum of: {}", - file.display())) + })().chain_err(|| { + format!("failed to calculate checksum of: {}", + file.display()) })?; let actual = h.finish().to_hex(); diff --git a/src/cargo/sources/git/source.rs b/src/cargo/sources/git/source.rs index 326fa94f247..634c1814bb6 100644 --- a/src/cargo/sources/git/source.rs +++ b/src/cargo/sources/git/source.rs @@ -5,7 +5,8 @@ use url::Url; use core::source::{Source, SourceId}; use core::GitReference; use core::{Package, PackageId, Summary, Registry, Dependency}; -use util::{CargoResult, Config}; +use util::Config; +use util::errors::{CargoError, CargoResult}; use util::hex::short_hash; use sources::PathSource; use sources::git::utils::{GitRemote, GitRevision}; @@ -147,7 +148,7 @@ impl<'cfg> Source for GitSource<'cfg> { trace!("updating git source `{:?}`", self.remote); let repo = self.remote.checkout(&db_path, self.config)?; - let rev = repo.rev_for(&self.reference)?; + let rev = repo.rev_for(&self.reference).map_err(CargoError::into_internal)?; (repo, rev) } else { (self.remote.db_at(&db_path)?, actual_rev.unwrap()) diff --git a/src/cargo/sources/git/utils.rs b/src/cargo/sources/git/utils.rs index 7c8050c8f36..e715d63d112 100644 --- a/src/cargo/sources/git/utils.rs +++ b/src/cargo/sources/git/utils.rs @@ -8,7 +8,8 @@ use serde::ser::{self, Serialize}; use url::Url; use core::GitReference; -use util::{CargoResult, ChainError, human, ToUrl, internal, Config, network}; +use util::{ToUrl, internal, Config, network}; +use util::errors::{CargoResult, CargoResultExt, CargoError}; #[derive(PartialEq, Clone, Debug)] pub struct GitRevision(git2::Oid); @@ -90,14 +91,14 @@ impl GitRemote { pub fn checkout(&self, into: &Path, cargo_config: &Config) -> CargoResult { let repo = match git2::Repository::open(into) { Ok(repo) => { - self.fetch_into(&repo, cargo_config).chain_error(|| { - human(format!("failed to fetch into {}", into.display())) + self.fetch_into(&repo, cargo_config).chain_err(|| { + format!("failed to fetch into {}", into.display()) })?; repo } Err(..) => { - self.clone_into(into, cargo_config).chain_error(|| { - human(format!("failed to clone into: {}", into.display())) + self.clone_into(into, cargo_config).chain_err(|| { + format!("failed to clone into: {}", into.display()) })? } }; @@ -163,24 +164,24 @@ impl GitDatabase { pub fn rev_for(&self, reference: &GitReference) -> CargoResult { let id = match *reference { GitReference::Tag(ref s) => { - (|| { + (|| -> CargoResult { let refname = format!("refs/tags/{}", s); let id = self.repo.refname_to_id(&refname)?; let obj = self.repo.find_object(id, None)?; let obj = obj.peel(ObjectType::Commit)?; Ok(obj.id()) - }).chain_error(|| { - human(format!("failed to find tag `{}`", s)) + })().chain_err(|| { + format!("failed to find tag `{}`", s) })? } GitReference::Branch(ref s) => { (|| { let b = self.repo.find_branch(s, git2::BranchType::Local)?; - b.get().target().chain_error(|| { - human(format!("branch `{}` did not have a target", s)) + b.get().target().ok_or_else(|| { + CargoError::from(format!("branch `{}` did not have a target", s)) }) - }).chain_error(|| { - human(format!("failed to find branch `{}`", s)) + })().chain_err(|| { + format!("failed to find branch `{}`", s) })? } GitReference::Rev(ref s) => { @@ -231,20 +232,21 @@ impl<'a> GitCheckout<'a> { fn clone_repo(source: &Path, into: &Path) -> CargoResult { let dirname = into.parent().unwrap(); - fs::create_dir_all(&dirname).chain_error(|| { - human(format!("Couldn't mkdir {}", dirname.display())) + fs::create_dir_all(&dirname).chain_err(|| { + format!("Couldn't mkdir {}", dirname.display()) })?; if fs::metadata(&into).is_ok() { - fs::remove_dir_all(into).chain_error(|| { - human(format!("Couldn't rmdir {}", into.display())) + fs::remove_dir_all(into).chain_err(|| { + format!("Couldn't rmdir {}", into.display()) })?; } let url = source.to_url()?; let url = url.to_string(); - let repo = git2::Repository::clone(&url, into).chain_error(|| { - internal(format!("failed to clone {} into {}", source.display(), + let repo = git2::Repository::clone(&url, into) + .chain_err(|| { + internal(format!("failed to clone {} into {}", source.display(), into.display())) })?; Ok(repo) @@ -294,9 +296,11 @@ impl<'a> GitCheckout<'a> { info!("update submodules for: {:?}", repo.workdir().unwrap()); for mut child in repo.submodules()?.into_iter() { - update_submodule(repo, &mut child, cargo_config).chain_error(|| { - human(format!("failed to update submodule `{}`", - child.name().unwrap_or(""))) + update_submodule(repo, &mut child, cargo_config) + .map_err(CargoError::into_internal) + .chain_err(|| { + format!("failed to update submodule `{}`", + child.name().unwrap_or("")) })?; } Ok(()) @@ -306,7 +310,7 @@ impl<'a> GitCheckout<'a> { child: &mut git2::Submodule, cargo_config: &Config) -> CargoResult<()> { child.init(false)?; - let url = child.url().chain_error(|| { + let url = child.url().ok_or_else(|| { internal("non-utf8 url for submodule") })?; @@ -341,13 +345,13 @@ impl<'a> GitCheckout<'a> { // Fetch data from origin and reset to the head commit let refspec = "refs/heads/*:refs/heads/*"; - fetch(&repo, url, refspec, cargo_config).chain_error(|| { + fetch(&repo, url, refspec, cargo_config).chain_err(|| { internal(format!("failed to fetch submodule `{}` from {}", child.name().unwrap_or(""), url)) })?; - let obj = repo.find_object(head, None)?; - repo.reset(&obj, git2::ResetType::Hard, None)?; + repo.find_object(head, None) + .and_then(|obj| { repo.reset(&obj, git2::ResetType::Hard, None)})?; update_submodules(&repo, cargo_config) } } @@ -528,7 +532,7 @@ fn with_authentication(url: &str, cfg: &git2::Config, mut f: F) // In the case of an authentication failure (where we tried something) then // we try to give a more helpful error message about precisely what we // tried. - res.chain_error(|| { + res.map_err(CargoError::from).map_err(|e| e.into_internal()).chain_err(|| { let mut msg = "failed to authenticate when downloading \ repository".to_string(); if !ssh_agent_attempts.is_empty() { @@ -549,7 +553,7 @@ fn with_authentication(url: &str, cfg: &git2::Config, mut f: F) credentials were incorrect"); } } - human(msg) + msg }) } @@ -574,6 +578,7 @@ pub fn fetch(repo: &git2::Repository, network::with_retry(config, || { remote.fetch(&[refspec], Some(&mut opts), None) + .map_err(CargoError::from) })?; Ok(()) }) diff --git a/src/cargo/sources/path.rs b/src/cargo/sources/path.rs index 77bf165eb01..dd27076cf5d 100644 --- a/src/cargo/sources/path.rs +++ b/src/cargo/sources/path.rs @@ -8,7 +8,7 @@ use glob::Pattern; use core::{Package, PackageId, Summary, SourceId, Source, Dependency, Registry}; use ops; -use util::{self, CargoResult, internal, internal_error, human, ChainError}; +use util::{self, CargoError, CargoResult, internal}; use util::Config; pub struct PathSource<'cfg> { @@ -90,7 +90,7 @@ impl<'cfg> PathSource<'cfg> { let parse = |p: &String| { Pattern::new(p).map_err(|e| { - human(format!("could not parse pattern `{}`: {}", p, e)) + CargoError::from(format!("could not parse pattern `{}`: {}", p, e)) }) }; @@ -154,8 +154,8 @@ impl<'cfg> PathSource<'cfg> { -> CargoResult> { warn!("list_files_git {}", pkg.package_id()); let index = repo.index()?; - let root = repo.workdir().chain_error(|| { - internal_error("Can't list files on a bare repository.", "") + let root = repo.workdir().ok_or_else(|| { + internal("Can't list files on a bare repository.") })?; let pkg_path = pkg.root(); @@ -230,8 +230,8 @@ impl<'cfg> PathSource<'cfg> { if is_dir.unwrap_or_else(|| file_path.is_dir()) { warn!(" found submodule {}", file_path.display()); let rel = util::without_prefix(&file_path, root).unwrap(); - let rel = rel.to_str().chain_error(|| { - human(format!("invalid utf-8 filename: {}", rel.display())) + let rel = rel.to_str().ok_or_else(|| { + CargoError::from(format!("invalid utf-8 filename: {}", rel.display())) })?; // Git submodules are currently only named through `/` path // separators, explicitly not `\` which windows uses. Who knew? @@ -348,7 +348,7 @@ impl<'cfg> Source for PathSource<'cfg> { fn fingerprint(&self, pkg: &Package) -> CargoResult { if !self.updated { - return Err(internal_error("BUG: source was not updated", "")); + return Err(internal("BUG: source was not updated")); } let mut max = FileTime::zero(); diff --git a/src/cargo/sources/registry/index.rs b/src/cargo/sources/registry/index.rs index 026f20204a9..5853774b20a 100644 --- a/src/cargo/sources/registry/index.rs +++ b/src/cargo/sources/registry/index.rs @@ -8,8 +8,7 @@ use core::dependency::{Dependency, DependencyInner, Kind}; use core::{SourceId, Summary, PackageId, Registry}; use sources::registry::{RegistryPackage, RegistryDependency, INDEX_LOCK}; use sources::registry::RegistryData; -use util::{CargoResult, ChainError, internal, Filesystem, Config}; -use util::human; +use util::{CargoError, CargoResult, internal, Filesystem, Config}; pub struct RegistryIndex<'cfg> { source_id: SourceId, @@ -47,7 +46,7 @@ impl<'cfg> RegistryIndex<'cfg> { } // Ok, we're missing the key, so parse the index file to load it. self.summaries(pkg.name(), load)?; - self.hashes.get(&key).chain_error(|| { + self.hashes.get(&key).ok_or_else(|| { internal(format!("no hash listed for {}", pkg)) }).map(|s| s.clone()) } @@ -107,7 +106,7 @@ impl<'cfg> RegistryIndex<'cfg> { match load.load(&root, Path::new(&path)) { Ok(contents) => { let contents = str::from_utf8(&contents).map_err(|_| { - human("registry index file was not valid utf-8") + CargoError::from("registry index file was not valid utf-8") })?; let lines = contents.lines() .map(|s| s.trim()) diff --git a/src/cargo/sources/registry/local.rs b/src/cargo/sources/registry/local.rs index fa4cbcd82d1..ef23552caf1 100644 --- a/src/cargo/sources/registry/local.rs +++ b/src/cargo/sources/registry/local.rs @@ -8,7 +8,8 @@ use core::PackageId; use sources::registry::{RegistryData, RegistryConfig}; use util::FileLock; use util::paths; -use util::{Config, CargoResult, ChainError, human, Sha256, Filesystem}; +use util::{Config, Sha256, Filesystem}; +use util::errors::{CargoResult, CargoResultExt}; pub struct LocalRegistry<'cfg> { index_path: Filesystem, @@ -83,8 +84,8 @@ impl<'cfg> RegistryData for LocalRegistry<'cfg> { let mut state = Sha256::new(); let mut buf = [0; 64 * 1024]; loop { - let n = crate_file.read(&mut buf).chain_error(|| { - human(format!("failed to read `{}`", crate_file.path().display())) + let n = crate_file.read(&mut buf).chain_err(|| { + format!("failed to read `{}`", crate_file.path().display()) })?; if n == 0 { break diff --git a/src/cargo/sources/registry/mod.rs b/src/cargo/sources/registry/mod.rs index 2af4ad9719f..5319c4bfe6e 100644 --- a/src/cargo/sources/registry/mod.rs +++ b/src/cargo/sources/registry/mod.rs @@ -168,7 +168,8 @@ use tar::Archive; use core::{Source, SourceId, PackageId, Package, Summary, Registry}; use core::dependency::Dependency; use sources::PathSource; -use util::{CargoResult, Config, internal, ChainError, FileLock, Filesystem}; +use util::{CargoResult, Config, internal, FileLock, Filesystem}; +use util::errors::CargoResultExt; use util::hex; const INDEX_LOCK: &'static str = ".cargo-index-lock"; @@ -359,7 +360,7 @@ impl<'cfg> Source for RegistrySource<'cfg> { fn download(&mut self, package: &PackageId) -> CargoResult { let hash = self.index.hash(package, &mut *self.ops)?; let path = self.ops.download(package, &hash)?; - let path = self.unpack_package(package, &path).chain_error(|| { + let path = self.unpack_package(package, &path).chain_err(|| { internal(format!("failed to unpack package `{}`", package)) })?; let mut src = PathSource::new(&path, &self.source_id, self.config); diff --git a/src/cargo/sources/registry/remote.rs b/src/cargo/sources/registry/remote.rs index cac0c7a8be3..cddbda2ed62 100644 --- a/src/cargo/sources/registry/remote.rs +++ b/src/cargo/sources/registry/remote.rs @@ -16,8 +16,8 @@ use sources::git; use sources::registry::{RegistryData, RegistryConfig, INDEX_LOCK}; use util::network; use util::{FileLock, Filesystem, LazyCell}; -use util::{Config, CargoResult, ChainError, human, Sha256, ToUrl}; -use util::errors::HttpError; +use util::{Config, Sha256, ToUrl}; +use util::errors::{CargoErrorKind, CargoResult, CargoResultExt}; pub struct RemoteRegistry<'cfg> { index_path: Filesystem, @@ -189,10 +189,11 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> { // git fetch origin master let url = self.source_id.url().to_string(); let refspec = "refs/heads/master:refs/remotes/origin/master"; - git::fetch(&repo, &url, refspec, self.config).chain_error(|| { - human(format!("failed to fetch `{}`", url)) + git::fetch(&repo, &url, refspec, self.config).chain_err(|| { + format!("failed to fetch `{}`", url) })?; } + self.head.set(None); *self.tree.borrow_mut() = None; Ok(()) @@ -254,7 +255,7 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> { let code = handle.response_code()?; if code != 200 && code != 0 { let url = handle.effective_url()?.unwrap_or(&url); - Err(HttpError::Not200(code, url.to_string())) + Err(CargoErrorKind::HttpNot200(code, url.to_string()).into()) } else { Ok(()) } diff --git a/src/cargo/sources/replaced.rs b/src/cargo/sources/replaced.rs index 1e682b355f3..47d9feb68ad 100644 --- a/src/cargo/sources/replaced.rs +++ b/src/cargo/sources/replaced.rs @@ -1,5 +1,5 @@ use core::{Source, Registry, PackageId, Package, Dependency, Summary, SourceId}; -use util::{CargoResult, ChainError, human}; +use util::errors::{CargoResult, CargoResultExt}; pub struct ReplacedSource<'cfg> { to_replace: SourceId, @@ -22,9 +22,9 @@ impl<'cfg> ReplacedSource<'cfg> { impl<'cfg> Registry for ReplacedSource<'cfg> { fn query(&mut self, dep: &Dependency) -> CargoResult> { let dep = dep.clone().map_source(&self.to_replace, &self.replace_with); - let ret = self.inner.query(&dep).chain_error(|| { - human(format!("failed to query replaced source `{}`", - self.to_replace)) + let ret = self.inner.query(&dep).chain_err(|| { + format!("failed to query replaced source `{}`", + self.to_replace) })?; Ok(ret.into_iter().map(|summary| { summary.map_source(&self.replace_with, &self.to_replace) @@ -38,17 +38,17 @@ impl<'cfg> Source for ReplacedSource<'cfg> { } fn update(&mut self) -> CargoResult<()> { - self.inner.update().chain_error(|| { - human(format!("failed to update replaced source `{}`", - self.to_replace)) + self.inner.update().chain_err(|| { + format!("failed to update replaced source `{}`", + self.to_replace) }) } fn download(&mut self, id: &PackageId) -> CargoResult { let id = id.with_source_id(&self.replace_with); - let pkg = self.inner.download(&id).chain_error(|| { - human(format!("failed to download replaced source `{}`", - self.to_replace)) + let pkg = self.inner.download(&id).chain_err(|| { + format!("failed to download replaced source `{}`", + self.to_replace) })?; Ok(pkg.map_source(&self.replace_with, &self.to_replace)) } diff --git a/src/cargo/util/cfg.rs b/src/cargo/util/cfg.rs index a666a07edb0..341b24d6d93 100644 --- a/src/cargo/util/cfg.rs +++ b/src/cargo/util/cfg.rs @@ -2,7 +2,7 @@ use std::str::{self, FromStr}; use std::iter; use std::fmt; -use util::{CargoError, CargoResult, human}; +use util::{CargoError, CargoResult}; #[derive(Clone, PartialEq, Debug)] pub enum Cfg { @@ -38,7 +38,7 @@ struct Parser<'a> { } impl FromStr for Cfg { - type Err = Box; + type Err = CargoError; fn from_str(s: &str) -> CargoResult { let mut p = Parser::new(s); @@ -71,7 +71,7 @@ impl CfgExpr { } impl FromStr for CfgExpr { - type Err = Box; + type Err = CargoError; fn from_str(s: &str) -> CargoResult { let mut p = Parser::new(s); @@ -215,7 +215,7 @@ impl<'a> Iterator for Tokenizer<'a> { return Some(Ok(Token::String(&self.orig[start+1..end]))) } } - return Some(Err(human("unterminated string in cfg".to_string()))) + return Some(Err("unterminated string in cfg".into())) } Some((start, ch)) if is_ident_start(ch) => { while let Some(&(end, ch)) = self.s.peek() { @@ -228,10 +228,10 @@ impl<'a> Iterator for Tokenizer<'a> { return Some(Ok(Token::Ident(&self.orig[start..]))) } Some((_, ch)) => { - return Some(Err(human(format!("unexpected character in \ + return Some(Err(format!("unexpected character in \ cfg `{}`, expected parens, \ a comma, an identifier, or \ - a string", ch)))) + a string", ch).into())) } None => return None } diff --git a/src/cargo/util/config.rs b/src/cargo/util/config.rs index 269384145b3..afaacae0674 100644 --- a/src/cargo/util/config.rs +++ b/src/cargo/util/config.rs @@ -15,7 +15,8 @@ use rustc_serialize::{Encodable,Encoder}; use toml; use core::shell::{Verbosity, ColorConfig}; use core::MultiShell; -use util::{CargoResult, CargoError, ChainError, Rustc, internal, human}; +use util::Rustc; +use util::errors::{CargoResult, CargoResultExt, CargoError, internal}; use util::{Filesystem, LazyCell}; use util::paths; @@ -56,12 +57,12 @@ impl Config { pub fn default() -> CargoResult { let shell = ::shell(Verbosity::Verbose, ColorConfig::Auto); - let cwd = env::current_dir().chain_error(|| { - human("couldn't get the current directory of the process") + let cwd = env::current_dir().chain_err(|| { + "couldn't get the current directory of the process" })?; - let homedir = homedir(&cwd).chain_error(|| { - human("Cargo couldn't find your home directory. \ - This probably means that $HOME was not set.") + let homedir = homedir(&cwd).ok_or_else(|| { + "Cargo couldn't find your home directory. \ + This probably means that $HOME was not set." })?; Ok(Config::new(shell, cwd, homedir)) } @@ -100,9 +101,7 @@ impl Config { pub fn cargo_exe(&self) -> CargoResult<&Path> { self.cargo_exe.get_or_try_init(|| env::current_exe().and_then(|path| path.canonicalize()) - .chain_error(|| { - human("couldn't get the path to cargo executable") - }) + .chain_err(|| "couldn't get the path to cargo executable") ).map(AsRef::as_ref) } @@ -112,11 +111,11 @@ impl Config { pub fn set_values(&self, values: HashMap) -> CargoResult<()> { if self.values.borrow().is_some() { - return Err(human("Config values already found")); + return Err("Config values already found".into()); } match self.values.fill(values) { Ok(()) => Ok(()), - Err(_) => Err(human("Could not fill values")), + Err(_) => Err("Could not fill values".into()), } } @@ -165,7 +164,7 @@ impl Config { } fn get_env(&self, key: &str) -> CargoResult>> - where Box: From + where CargoError: From { let key = key.replace(".", "_") .replace("-", "_") @@ -341,7 +340,7 @@ impl Config { pub fn expected(&self, ty: &str, key: &str, val: CV) -> CargoResult { val.expected(ty, key).map_err(|e| { - human(format!("invalid configuration for key `{}`\n{}", key, e)) + format!("invalid configuration for key `{}`\n{}", key, e).into() }) } @@ -410,26 +409,25 @@ impl Config { walk_tree(&self.cwd, |mut file, path| { let mut contents = String::new(); - file.read_to_string(&mut contents).chain_error(|| { - human(format!("failed to read configuration file `{}`", - path.display())) + file.read_to_string(&mut contents).chain_err(|| { + format!("failed to read configuration file `{}`", + path.display()) })?; let toml = cargo_toml::parse(&contents, &path, - self).chain_error(|| { - human(format!("could not parse TOML configuration in `{}`", - path.display())) + self).chain_err(|| { + format!("could not parse TOML configuration in `{}`", + path.display()) })?; - let value = CV::from_toml(&path, toml).chain_error(|| { - human(format!("failed to load TOML configuration from `{}`", - path.display())) + let value = CV::from_toml(&path, toml).chain_err(|| { + format!("failed to load TOML configuration from `{}`", + path.display()) })?; - cfg.merge(value).chain_error(|| { - human(format!("failed to merge configuration at `{}`", - path.display())) + cfg.merge(value).chain_err(|| { + format!("failed to merge configuration at `{}`", path.display()) })?; Ok(()) - }).chain_error(|| human("Couldn't load Cargo configuration"))?; + }).chain_err(|| "Couldn't load Cargo configuration")?; match cfg { @@ -534,15 +532,15 @@ impl ConfigValue { Ok(CV::List(val.into_iter().map(|toml| { match toml { toml::Value::String(val) => Ok((val, path.to_path_buf())), - v => Err(human(format!("expected string but found {} \ - in list", v.type_str()))), + v => Err(format!("expected string but found {} \ + in list", v.type_str()).into()), } }).collect::>()?, path.to_path_buf())) } toml::Value::Table(val) => { Ok(CV::Table(val.into_iter().map(|(key, value)| { - let value = CV::from_toml(path, value).chain_error(|| { - human(format!("failed to parse key `{}`", key)) + let value = CV::from_toml(path, value).chain_err(|| { + format!("failed to parse key `{}`", key) })?; Ok((key, value)) }).collect::>()?, path.to_path_buf())) @@ -568,14 +566,14 @@ impl ConfigValue { Occupied(mut entry) => { let path = value.definition_path().to_path_buf(); let entry = entry.get_mut(); - entry.merge(value).chain_error(|| { - human(format!("failed to merge key `{}` between \ - files:\n \ - file 1: {}\n \ - file 2: {}", - key, - entry.definition_path().display(), - path.display())) + entry.merge(value).chain_err(|| { + format!("failed to merge key `{}` between \ + files:\n \ + file 1: {}\n \ + file 2: {}", + key, + entry.definition_path().display(), + path.display()) })?; } @@ -649,9 +647,9 @@ impl ConfigValue { } fn expected(&self, wanted: &str, key: &str) -> CargoResult { - Err(human(format!("expected a {}, but found a {} for `{}` in {}", - wanted, self.desc(), key, - self.definition_path().display()))) + Err(format!("expected a {}, but found a {} for `{}` in {}", + wanted, self.desc(), key, + self.definition_path().display()).into()) } fn into_toml(self) -> toml::Value { @@ -757,9 +755,9 @@ fn walk_tree(pwd: &Path, mut walk: F) -> CargoResult<()> // Once we're done, also be sure to walk the home directory even if it's not // in our history to be sure we pick up that standard location for // information. - let home = homedir(pwd).chain_error(|| { - human("Cargo couldn't find your home directory. \ - This probably means that $HOME was not set.") + let home = homedir(pwd).ok_or_else(|| { + CargoError::from("Cargo couldn't find your home directory. \ + This probably means that $HOME was not set.") })?; let config = home.join("config"); if !stash.contains(&config) && fs::metadata(&config).is_ok() { diff --git a/src/cargo/util/errors.rs b/src/cargo/util/errors.rs index 0c621524b72..11d6dfdc6bf 100644 --- a/src/cargo/util/errors.rs +++ b/src/cargo/util/errors.rs @@ -1,5 +1,4 @@ use std::error::Error; -use std::ffi; use std::fmt; use std::io; use std::num; @@ -11,134 +10,100 @@ use core::TargetKind; use curl; use git2; -use glob; use semver; use serde_json; use term; use toml; -use url; +use registry; -pub type CargoResult = Result>; - -// ============================================================================= -// CargoError trait - -pub trait CargoError: Error + Send + 'static { - fn is_human(&self) -> bool { false } - fn cargo_cause(&self) -> Option<&CargoError>{ None } - fn as_error(&self) -> &Error where Self: Sized { self as &Error } -} - -impl Error for Box { - fn description(&self) -> &str { (**self).description() } - fn cause(&self) -> Option<&Error> { (**self).cause() } -} - -impl CargoError for Box { - fn is_human(&self) -> bool { (**self).is_human() } - fn cargo_cause(&self) -> Option<&CargoError> { (**self).cargo_cause() } -} - -// ============================================================================= -// Chaining errors - -pub trait ChainError { - fn chain_error(self, callback: F) -> CargoResult - where E: CargoError, F: FnOnce() -> E; -} - -#[derive(Debug)] -struct ChainedError { - error: E, - cause: Box, -} - -impl<'a, T, F> ChainError for F where F: FnOnce() -> CargoResult { - fn chain_error(self, callback: C) -> CargoResult - where E: CargoError, C: FnOnce() -> E { - self().chain_error(callback) +error_chain! { + types { + CargoError, CargoErrorKind, CargoResultExt, CargoResult; } -} -impl ChainError for Result { - fn chain_error(self, callback: C) -> CargoResult - where E2: CargoError, C: FnOnce() -> E2 { - self.map_err(move |err| { - Box::new(ChainedError { - error: callback(), - cause: Box::new(err), - }) as Box - }) + links { + CrateRegistry(registry::Error, registry::ErrorKind); } -} -impl ChainError for Box { - fn chain_error(self, callback: C) -> CargoResult - where E2: CargoError, C: FnOnce() -> E2 { - Err(Box::new(ChainedError { - error: callback(), - cause: self, - })) + foreign_links { + ParseSemver(semver::ReqParseError); + Semver(semver::SemVerError); + Io(io::Error); + SerdeJson(serde_json::Error); + TomlSer(toml::ser::Error); + TomlDe(toml::de::Error); + Term(term::Error); + ParseInt(num::ParseIntError); + ParseBool(str::ParseBoolError); + Parse(string::ParseError); + Git(git2::Error); + Curl(curl::Error); } -} -impl ChainError for Option { - fn chain_error(self, callback: C) -> CargoResult - where E: CargoError, C: FnOnce() -> E { - match self { - Some(t) => Ok(t), - None => Err(Box::new(callback())), + errors { + Internal(err: Box) { + description(err.description()) + display("{}", *err) + } + ProcessErrorKind(proc_err: ProcessError) { + description(&proc_err.desc) + display("{}", &proc_err.desc) + } + CargoTestErrorKind(test_err: CargoTestError) { + description(&test_err.desc) + display("{}", &test_err.desc) + } + HttpNot200(code: u32, url: String) { + description("failed to get a 200 response") + display("failed to get 200 response from `{}`, got {}", url, code) } } } -impl Error for ChainedError { - fn description(&self) -> &str { self.error.description() } -} +impl CargoError { + pub fn into_internal(self) -> Self { + CargoError(CargoErrorKind::Internal(Box::new(self.0)), self.1) + } -impl fmt::Display for ChainedError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&self.error, f) + fn is_human(&self) -> bool { + match &self.0 { + &CargoErrorKind::Msg(_) => true, + &CargoErrorKind::TomlSer(_) => true, + &CargoErrorKind::TomlDe(_) => true, + &CargoErrorKind::Curl(_) => true, + &CargoErrorKind::HttpNot200(..) => true, + &CargoErrorKind::ProcessErrorKind(_) => true, + &CargoErrorKind::CrateRegistry(_) | + &CargoErrorKind::ParseSemver(_) | + &CargoErrorKind::Semver(_) | + &CargoErrorKind::Io(_) | + &CargoErrorKind::SerdeJson(_) | + &CargoErrorKind::Term(_) | + &CargoErrorKind::ParseInt(_) | + &CargoErrorKind::ParseBool(_) | + &CargoErrorKind::Parse(_) | + &CargoErrorKind::Git(_) | + &CargoErrorKind::Internal(_) | + &CargoErrorKind::CargoTestErrorKind(_) => false + } } } -impl CargoError for ChainedError { - fn is_human(&self) -> bool { self.error.is_human() } - fn cargo_cause(&self) -> Option<&CargoError> { Some(&*self.cause) } -} // ============================================================================= // Process errors - +#[derive(Debug)] pub struct ProcessError { pub desc: String, pub exit: Option, pub output: Option, - cause: Option>, -} - -impl Error for ProcessError { - fn description(&self) -> &str { &self.desc } - fn cause(&self) -> Option<&Error> { - self.cause.as_ref().map(|e| e.as_error()) - } -} - -impl fmt::Display for ProcessError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&self.desc, f) - } -} -impl fmt::Debug for ProcessError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self, f) - } } // ============================================================================= // Cargo test errors. /// Error when testcases fail +#[derive(Debug)] pub struct CargoTestError { pub test: Test, pub desc: String, @@ -146,6 +111,7 @@ pub struct CargoTestError { pub causes: Vec, } +#[derive(Debug)] pub enum Test { Multiple, Doc, @@ -187,88 +153,6 @@ impl CargoTestError { } } -impl fmt::Display for CargoTestError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&self.desc, f) - } -} - -impl fmt::Debug for CargoTestError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self, f) - } -} - -impl Error for CargoTestError { - fn description(&self) -> &str { &self.desc } - fn cause(&self) -> Option<&Error> { - self.causes.get(0).map(|s| s as &Error) - } -} - - -// ============================================================================= -// Concrete errors - -struct ConcreteCargoError { - description: String, - detail: Option, - cause: Option>, - is_human: bool, -} - -impl fmt::Display for ConcreteCargoError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.description)?; - if let Some(ref s) = self.detail { - write!(f, " ({})", s)?; - } - Ok(()) - } -} -impl fmt::Debug for ConcreteCargoError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self, f) - } -} - -impl Error for ConcreteCargoError { - fn description(&self) -> &str { &self.description } - fn cause(&self) -> Option<&Error> { - self.cause.as_ref().map(|c| { - let e: &Error = &**c; e - }) - } -} - -impl CargoError for ConcreteCargoError { - fn is_human(&self) -> bool { - self.is_human - } -} - -// ============================================================================= -// Human errors - -#[derive(Debug)] -pub struct Human(pub E); - -impl Error for Human { - fn description(&self) -> &str { self.0.description() } - fn cause(&self) -> Option<&Error> { self.0.cause() } -} - -impl fmt::Display for Human { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&self.0, f) - } -} - -impl CargoError for Human { - fn is_human(&self) -> bool { true } - fn cargo_cause(&self) -> Option<&CargoError> { self.0.cargo_cause() } -} - // ============================================================================= // CLI errors @@ -276,7 +160,7 @@ pub type CliResult = Result<(), CliError>; #[derive(Debug)] pub struct CliError { - pub error: Option>, + pub error: Option, pub unknown: bool, pub exit_code: i32 } @@ -303,8 +187,8 @@ impl fmt::Display for CliError { } impl CliError { - pub fn new(error: Box, code: i32) -> CliError { - let human = error.is_human(); + pub fn new(error: CargoError, code: i32) -> CliError { + let human = &error.is_human(); CliError { error: Some(error), exit_code: code, unknown: !human } } @@ -313,163 +197,17 @@ impl CliError { } } -impl From> for CliError { - fn from(err: Box) -> CliError { +impl From for CliError { + fn from(err: CargoError) -> CliError { CliError::new(err, 101) } } -// ============================================================================= -// NetworkError trait - -pub trait NetworkError: CargoError { - fn maybe_spurious(&self) -> bool; -} - -impl NetworkError for git2::Error { - fn maybe_spurious(&self) -> bool { - match self.class() { - git2::ErrorClass::Net | - git2::ErrorClass::Os => true, - _ => false - } - } -} - -impl NetworkError for curl::Error { - fn maybe_spurious(&self) -> bool { - self.is_couldnt_connect() || - self.is_couldnt_resolve_proxy() || - self.is_couldnt_resolve_host() || - self.is_operation_timedout() || - self.is_recv_error() - } -} - -#[derive(Debug)] -pub enum HttpError { - Not200(u32, String), - Curl(curl::Error), -} - -impl fmt::Display for HttpError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - HttpError::Not200(code, ref url) => { - write!(f, "failed to get 200 response from `{}`, got {}", - url, code) - } - HttpError::Curl(ref e) => e.fmt(f), - } - } -} - -impl Error for HttpError { - fn description(&self) -> &str { - match *self { - HttpError::Not200(..) => "failed to get a 200 response", - HttpError::Curl(ref e) => e.description(), - } - } - - fn cause(&self) -> Option<&Error> { - match *self { - HttpError::Not200(..) => None, - HttpError::Curl(ref e) => e.cause(), - } - } -} - -impl CargoError for HttpError { - fn is_human(&self) -> bool { - true - } -} - -impl NetworkError for HttpError { - fn maybe_spurious(&self) -> bool { - match *self { - HttpError::Not200(code, ref _url) => { - 500 <= code && code < 600 - } - HttpError::Curl(ref e) => e.maybe_spurious(), - } - } -} - -impl From for HttpError { - fn from(err: curl::Error) -> HttpError { - HttpError::Curl(err) - } -} - -// ============================================================================= -// various impls - -macro_rules! from_error { - ($($p:ty,)*) => ( - $(impl From<$p> for Box { - fn from(t: $p) -> Box { Box::new(t) } - })* - ) -} - -from_error! { - semver::ReqParseError, - io::Error, - ProcessError, - git2::Error, - serde_json::Error, - curl::Error, - CliError, - url::ParseError, - toml::ser::Error, - toml::de::Error, - ffi::NulError, - term::Error, - num::ParseIntError, - str::ParseBoolError, - glob::PatternError, - glob::GlobError, -} - -impl From for Box { - fn from(t: string::ParseError) -> Box { - match t {} - } -} - -impl From> for Box { - fn from(t: Human) -> Box { Box::new(t) } -} - -impl CargoError for semver::ReqParseError {} -impl CargoError for io::Error {} -impl CargoError for git2::Error {} -impl CargoError for serde_json::Error {} -impl CargoError for curl::Error {} -impl CargoError for ProcessError {} -impl CargoError for CargoTestError {} -impl CargoError for CliError {} -impl CargoError for toml::ser::Error { - fn is_human(&self) -> bool { true } -} -impl CargoError for toml::de::Error { - fn is_human(&self) -> bool { true } -} -impl CargoError for url::ParseError {} -impl CargoError for ffi::NulError {} -impl CargoError for term::Error {} -impl CargoError for num::ParseIntError {} -impl CargoError for str::ParseBoolError {} -impl CargoError for glob::PatternError {} -impl CargoError for glob::GlobError {} // ============================================================================= // Construction helpers pub fn process_error(msg: &str, - cause: Option>, status: Option<&ExitStatus>, output: Option<&Output>) -> ProcessError { @@ -500,7 +238,6 @@ pub fn process_error(msg: &str, desc: desc, exit: status.cloned(), output: output.cloned(), - cause: cause, }; #[cfg(unix)] @@ -539,49 +276,10 @@ pub fn process_error(msg: &str, } } -pub fn internal_error(error: &str, detail: &str) -> Box { - Box::new(ConcreteCargoError { - description: error.to_string(), - detail: Some(detail.to_string()), - cause: None, - is_human: false - }) -} - -pub fn internal(error: S) -> Box { +pub fn internal(error: S) -> CargoError { _internal(&error) } -fn _internal(error: &fmt::Display) -> Box { - Box::new(ConcreteCargoError { - description: error.to_string(), - detail: None, - cause: None, - is_human: false - }) -} - -pub fn human(error: S) -> Box { - _human(&error) -} - -fn _human(error: &fmt::Display) -> Box { - Box::new(ConcreteCargoError { - description: error.to_string(), - detail: None, - cause: None, - is_human: true - }) -} - -pub fn caused_human(error: S, cause: E) -> Box - where S: fmt::Display, - E: Error + Send + 'static -{ - Box::new(ConcreteCargoError { - description: error.to_string(), - detail: None, - cause: Some(Box::new(cause)), - is_human: true - }) +fn _internal(error: &fmt::Display) -> CargoError { + CargoError::from_kind(error.to_string().into()).into_internal() } diff --git a/src/cargo/util/flock.rs b/src/cargo/util/flock.rs index 861dd4f7357..1be77b2d843 100644 --- a/src/cargo/util/flock.rs +++ b/src/cargo/util/flock.rs @@ -8,7 +8,8 @@ use fs2::{FileExt, lock_contended_error}; #[allow(unused_imports)] use libc; -use util::{CargoResult, ChainError, Config, human}; +use util::Config; +use util::errors::{CargoResult, CargoResultExt}; pub struct FileLock { f: Option, @@ -211,8 +212,8 @@ impl Filesystem { } else { Err(e) } - }).chain_error(|| { - human(format!("failed to open: {}", path.display())) + }).chain_err(|| { + format!("failed to open: {}", path.display()) })?; match state { State::Exclusive => { @@ -281,8 +282,8 @@ fn acquire(config: &Config, Err(e) => { if e.raw_os_error() != lock_contended_error().raw_os_error() { - return Err(human(e)).chain_error(|| { - human(format!("failed to lock file: {}", path.display())) + return Err(e).chain_err(|| { + format!("failed to lock file: {}", path.display()) }) } } @@ -290,8 +291,8 @@ fn acquire(config: &Config, let msg = format!("waiting for file lock on {}", msg); config.shell().status_with_color("Blocking", &msg, CYAN)?; - return block().chain_error(|| { - human(format!("failed to lock file: {}", path.display())) + return block().chain_err(|| { + format!("failed to lock file: {}", path.display()) }); #[cfg(all(target_os = "linux", not(target_env = "musl")))] diff --git a/src/cargo/util/important_paths.rs b/src/cargo/util/important_paths.rs index 35161eaae1d..069979ea97c 100644 --- a/src/cargo/util/important_paths.rs +++ b/src/cargo/util/important_paths.rs @@ -1,6 +1,6 @@ use std::fs; use std::path::{Path, PathBuf}; -use util::{CargoResult, human}; +use util::errors::CargoResult; use util::paths; /// Iteratively search for `file` in `pwd` and its parents, returning @@ -59,7 +59,7 @@ pub fn find_project_manifest_exact(pwd: &Path, file: &str) -> CargoResult(err: &E) -> bool + where E: ChainedError + 'static { + //Error inspection in non-verbose mode requires inspecting the + //error kind to avoid printing Internal errors. The downcasting + //machinery requires &(Error + 'static), but the iterator (and + //underlying `cause`) return &Error. Because the borrows are + //constrained to this handling method, and because the original + //error object is constrained to be 'static, we're casting away + //the borrow's actual lifetime for purposes of downcasting and + //inspecting the error chain + unsafe fn extend_lifetime(r: &Error) -> &(Error + 'static) { + std::mem::transmute::<&Error, &Error>(r) + } + + for e in err.iter() { + let e = unsafe { extend_lifetime(e) }; + if let Some(cargo_err) = e.downcast_ref::() { + match cargo_err.kind() { + &CargoErrorKind::Git(ref git_err) => { + match git_err.class() { + git2::ErrorClass::Net | + git2::ErrorClass::Os => return true, + _ => () + } + } + &CargoErrorKind::Curl(ref curl_err) + if curl_err.is_couldnt_connect() || + curl_err.is_couldnt_resolve_proxy() || + curl_err.is_couldnt_resolve_host() || + curl_err.is_operation_timedout() || + curl_err.is_recv_error() => { + return true + } + &CargoErrorKind::HttpNot200(code, ref _url) if 500 <= code && code < 600 => { + return true + } + _ => () + } + } + } + false +} /// Wrapper method for network call retry logic. /// @@ -13,76 +63,44 @@ use util::{CargoResult, Config, errors}; /// use util::network; /// cargo_result = network.with_retry(&config, || something.download()); /// ``` -pub fn with_retry(config: &Config, mut callback: F) -> CargoResult - where F: FnMut() -> Result, - E: errors::NetworkError +pub fn with_retry(config: &Config, mut callback: F) -> CargoResult + where F: FnMut() -> CargoResult { let mut remaining = config.net_retry()?; loop { match callback() { Ok(ret) => return Ok(ret), - Err(ref e) if e.maybe_spurious() && remaining > 0 => { + Err(ref e) if maybe_spurious(e) && remaining > 0 => { let msg = format!("spurious network error ({} tries \ remaining): {}", remaining, e); config.shell().warn(msg)?; remaining -= 1; } - Err(e) => return Err(Box::new(e)), + //todo impl from + Err(e) => return Err(e.into()), } } } #[test] fn with_retry_repeats_the_call_then_works() { + //Error HTTP codes (5xx) are considered maybe_spurious and will prompt retry + let error1 = CargoErrorKind::HttpNot200(501, "Uri".to_string()).into(); + let error2 = CargoErrorKind::HttpNot200(502, "Uri".to_string()).into(); + let mut results: Vec> = vec![Ok(()), Err(error1), Err(error2)]; + let config = Config::default().unwrap(); + let result = with_retry(&config, || results.pop().unwrap()); + assert_eq!(result.unwrap(), ()) +} - use std::error::Error; - use util::human; - use std::fmt; - - #[derive(Debug)] - struct NetworkRetryError { - error: Box, - } - - impl Error for NetworkRetryError { - fn description(&self) -> &str { - self.error.description() - } - fn cause(&self) -> Option<&Error> { - self.error.cause() - } - } - - impl NetworkRetryError { - fn new(error: &str) -> NetworkRetryError { - let error = human(error.to_string()); - NetworkRetryError { - error: error, - } - } - } - - impl fmt::Display for NetworkRetryError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&self.error, f) - } - } - - impl errors::CargoError for NetworkRetryError { - fn is_human(&self) -> bool { - false - } - } - - impl errors::NetworkError for NetworkRetryError { - fn maybe_spurious(&self) -> bool { - true - } - } - - let error1 = NetworkRetryError::new("one"); - let error2 = NetworkRetryError::new("two"); - let mut results: Vec> = vec![Ok(()), - Err(error1), Err(error2)]; +#[test] +fn with_retry_finds_nested_spurious_errors() { + //Error HTTP codes (5xx) are considered maybe_spurious and will prompt retry + //String error messages are not considered spurious + let error1 : CargoError = CargoErrorKind::HttpNot200(501, "Uri".to_string()).into(); + let error1 = CargoError::with_chain(error1, "A non-spurious wrapping err"); + let error2 = CargoError::from_kind(CargoErrorKind::HttpNot200(502, "Uri".to_string())); + let error2 = CargoError::with_chain(error2, "A second chained error"); + let mut results: Vec> = vec![Ok(()), Err(error1), Err(error2)]; let config = Config::default().unwrap(); let result = with_retry(&config, || results.pop().unwrap()); assert_eq!(result.unwrap(), ()) diff --git a/src/cargo/util/paths.rs b/src/cargo/util/paths.rs index 48273ae9263..fecc166a555 100644 --- a/src/cargo/util/paths.rs +++ b/src/cargo/util/paths.rs @@ -5,15 +5,16 @@ use std::fs::OpenOptions; use std::io::prelude::*; use std::path::{Path, PathBuf, Component}; -use util::{human, internal, CargoResult, ChainError}; +use util::{internal, CargoResult}; +use util::errors::CargoResultExt; pub fn join_paths>(paths: &[T], env: &str) -> CargoResult { env::join_paths(paths.iter()).or_else(|e| { let paths = paths.iter().map(Path::new).collect::>(); - internal(format!("failed to join path array: {:?}", paths)).chain_error(|| { - human(format!("failed to join search paths together: {}\n\ + Err(internal(format!("failed to join path array: {:?}", paths))).chain_err(|| { + format!("failed to join search paths together: {}\n\ Does ${} have an unterminated quote character?", - e, env)) + e, env) }) }) } @@ -73,8 +74,8 @@ pub fn read(path: &Path) -> CargoResult { let mut f = File::open(path)?; f.read_to_string(&mut ret)?; Ok(ret) - })().map_err(human).chain_error(|| { - human(format!("failed to read `{}`", path.display())) + })().chain_err(|| { + format!("failed to read `{}`", path.display()) }) } @@ -84,8 +85,8 @@ pub fn read_bytes(path: &Path) -> CargoResult> { let mut f = File::open(path)?; f.read_to_end(&mut ret)?; Ok(ret) - })().map_err(human).chain_error(|| { - human(format!("failed to read `{}`", path.display())) + })().chain_err(|| { + format!("failed to read `{}`", path.display()) }) } @@ -94,8 +95,8 @@ pub fn write(path: &Path, contents: &[u8]) -> CargoResult<()> { let mut f = File::create(path)?; f.write_all(contents)?; Ok(()) - })().map_err(human).chain_error(|| { - human(format!("failed to write `{}`", path.display())) + })().chain_err(|| { + format!("failed to write `{}`", path.display()) }) } @@ -109,7 +110,7 @@ pub fn append(path: &Path, contents: &[u8]) -> CargoResult<()> { f.write_all(contents)?; Ok(()) - }).chain_error(|| { + })().chain_err(|| { internal(format!("failed to write `{}`", path.display())) }) } @@ -123,8 +124,8 @@ pub fn path2bytes(path: &Path) -> CargoResult<&[u8]> { pub fn path2bytes(path: &Path) -> CargoResult<&[u8]> { match path.as_os_str().to_str() { Some(s) => Ok(s.as_bytes()), - None => Err(human(format!("invalid non-unicode path: {}", - path.display()))) + None => Err(format!("invalid non-unicode path: {}", + path.display()).into()) } } @@ -139,7 +140,7 @@ pub fn bytes2path(bytes: &[u8]) -> CargoResult { use std::str; match str::from_utf8(bytes) { Ok(s) => Ok(PathBuf::from(s)), - Err(..) => Err(human("invalid non-unicode path")), + Err(..) => Err("invalid non-unicode path".into()), } } diff --git a/src/cargo/util/process_builder.rs b/src/cargo/util/process_builder.rs index 26f872ed459..819771981ee 100644 --- a/src/cargo/util/process_builder.rs +++ b/src/cargo/util/process_builder.rs @@ -5,7 +5,8 @@ use std::fmt; use std::path::Path; use std::process::{Command, Stdio, Output}; -use util::{CargoResult, ProcessError, process_error, read2}; +use util::{CargoResult, CargoResultExt, CargoError, process_error, read2}; +use util::errors::CargoErrorKind; use shell_escape::escape; #[derive(Clone, PartialEq, Debug)] @@ -72,61 +73,62 @@ impl ProcessBuilder { pub fn get_envs(&self) -> &HashMap> { &self.env } - pub fn exec(&self) -> Result<(), ProcessError> { + pub fn exec(&self) -> CargoResult<()> { let mut command = self.build_command(); - let exit = command.status().map_err(|e| { - process_error(&format!("could not execute process `{}`", - self.debug_string()), - Some(Box::new(e)), None, None) + let exit = command.status().chain_err(|| { + CargoErrorKind::ProcessErrorKind( + process_error(&format!("could not execute process `{}`", + self.debug_string()), None, None)) })?; if exit.success() { Ok(()) } else { - Err(process_error(&format!("process didn't exit successfully: `{}`", - self.debug_string()), - None, Some(&exit), None)) + Err(CargoErrorKind::ProcessErrorKind(process_error( + &format!("process didn't exit successfully: `{}`", self.debug_string()), + Some(&exit), None)).into()) } } #[cfg(unix)] - pub fn exec_replace(&self) -> Result<(), ProcessError> { + pub fn exec_replace(&self) -> CargoResult<()> { use std::os::unix::process::CommandExt; let mut command = self.build_command(); let error = command.exec(); - Err(process_error(&format!("could not execute process `{}`", - self.debug_string()), - Some(Box::new(error)), None, None)) + Err(CargoError::with_chain(error, + CargoErrorKind::ProcessErrorKind(process_error( + &format!("could not execute process `{}`", self.debug_string()), None, None)))) } #[cfg(windows)] - pub fn exec_replace(&self) -> Result<(), ProcessError> { + pub fn exec_replace(&self) -> CargoResult<()> { self.exec() } - pub fn exec_with_output(&self) -> Result { + pub fn exec_with_output(&self) -> CargoResult { let mut command = self.build_command(); - let output = command.output().map_err(|e| { - process_error(&format!("could not execute process `{}`", - self.debug_string()), - Some(Box::new(e)), None, None) + let output = command.output().chain_err(|| { + CargoErrorKind::ProcessErrorKind( + process_error( + &format!("could not execute process `{}`", self.debug_string()), + None, None)) })?; if output.status.success() { Ok(output) } else { - Err(process_error(&format!("process didn't exit successfully: `{}`", - self.debug_string()), - None, Some(&output.status), Some(&output))) + Err(CargoErrorKind::ProcessErrorKind(process_error( + &format!("process didn't exit successfully: `{}`", self.debug_string()), + Some(&output.status), Some(&output))).into()) } } pub fn exec_with_streaming(&self, on_stdout_line: &mut FnMut(&str) -> CargoResult<()>, on_stderr_line: &mut FnMut(&str) -> CargoResult<()>) - -> Result { + -> CargoResult { let mut stdout = Vec::new(); let mut stderr = Vec::new(); @@ -166,10 +168,11 @@ impl ProcessBuilder { } })?; child.wait() - })().map_err(|e| { - process_error(&format!("could not execute process `{}`", - self.debug_string()), - Some(Box::new(e)), None, None) + })().chain_err(|| { + CargoErrorKind::ProcessErrorKind( + process_error(&format!("could not execute process `{}`", + self.debug_string()), + None, None)) })?; let output = Output { stdout: stdout, @@ -177,13 +180,14 @@ impl ProcessBuilder { status: status, }; if !output.status.success() { - Err(process_error(&format!("process didn't exit successfully: `{}`", - self.debug_string()), - None, Some(&output.status), Some(&output))) + Err(CargoErrorKind::ProcessErrorKind(process_error( + &format!("process didn't exit successfully: `{}`", self.debug_string()), + Some(&output.status), Some(&output))).into()) } else if let Some(e) = callback_error { - Err(process_error(&format!("failed to parse process output: `{}`", - self.debug_string()), - Some(Box::new(e)), Some(&output.status), Some(&output))) + Err(CargoError::with_chain(e, + CargoErrorKind::ProcessErrorKind(process_error( + &format!("failed to parse process output: `{}`", self.debug_string()), + Some(&output.status), Some(&output))))) } else { Ok(output) } diff --git a/src/cargo/util/rustc.rs b/src/cargo/util/rustc.rs index d401452d712..9118f2213fe 100644 --- a/src/cargo/util/rustc.rs +++ b/src/cargo/util/rustc.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use util::{self, CargoResult, internal, ChainError, ProcessBuilder}; +use util::{self, CargoResult, internal, ProcessBuilder}; pub struct Rustc { pub path: PathBuf, @@ -28,10 +28,7 @@ impl Rustc { let host = { let triple = verbose_version.lines().find(|l| { l.starts_with("host: ") - }).map(|l| &l[6..]); - let triple = triple.chain_error(|| { - internal("rustc -v didn't have a line for `host:`") - })?; + }).map(|l| &l[6..]).ok_or(internal("rustc -v didn't have a line for `host:`"))?; triple.to_string() }; diff --git a/src/cargo/util/to_url.rs b/src/cargo/util/to_url.rs index c250937b3a2..5508bb9aadf 100644 --- a/src/cargo/util/to_url.rs +++ b/src/cargo/util/to_url.rs @@ -2,7 +2,7 @@ use std::path::Path; use url::Url; -use util::{human, CargoResult}; +use util::CargoResult; pub trait ToUrl { fn to_url(self) -> CargoResult; @@ -11,7 +11,7 @@ pub trait ToUrl { impl<'a> ToUrl for &'a str { fn to_url(self) -> CargoResult { Url::parse(self).map_err(|s| { - human(format!("invalid url `{}`: {}", self, s)) + format!("invalid url `{}`: {}", self, s).into() }) } } @@ -19,7 +19,7 @@ impl<'a> ToUrl for &'a str { impl<'a> ToUrl for &'a Path { fn to_url(self) -> CargoResult { Url::from_file_path(self).map_err(|()| { - human(format!("invalid path url `{}`", self.display())) + format!("invalid path url `{}`", self.display()).into() }) } } diff --git a/src/cargo/util/toml.rs b/src/cargo/util/toml.rs index 5ffa4acff89..02c5e5eaefc 100644 --- a/src/cargo/util/toml.rs +++ b/src/cargo/util/toml.rs @@ -18,7 +18,8 @@ use core::dependency::{Kind, Platform}; use core::manifest::{LibKind, Profile, ManifestMetadata}; use ops::is_bad_artifact_name; use sources::CRATES_IO; -use util::{self, CargoResult, human, ToUrl, ChainError, Config}; +use util::{self, ToUrl, Config}; +use util::errors::{CargoError, CargoResult, CargoResultExt}; /// Representation of the projects file layout. /// @@ -189,8 +190,8 @@ in the future.", file.display()); return Ok(ret) } - Err(first_error).chain_error(|| { - human("could not parse input as TOML") + Err(first_error).chain_err(|| { + "could not parse input as TOML" }) } @@ -632,8 +633,8 @@ impl TomlManifest { let mut warnings = vec![]; let project = me.project.as_ref().or_else(|| me.package.as_ref()); - let project = project.chain_error(|| { - human("no `package` or `project` section found.") + let project = project.ok_or_else(|| { + CargoError::from("no `package` or `project` section found.") })?; if project.name.trim().is_empty() { @@ -941,10 +942,10 @@ impl TomlManifest { -> CargoResult> { let mut replace = Vec::new(); for (spec, replacement) in self.replace.iter().flat_map(|x| x) { - let mut spec = PackageIdSpec::parse(spec).chain_error(|| { - human(format!("replacements must specify a valid semver \ - version to replace, but `{}` does not", - spec)) + let mut spec = PackageIdSpec::parse(spec).chain_err(|| { + format!("replacements must specify a valid semver \ + version to replace, but `{}` does not", + spec) })?; if spec.url().is_none() { spec.set_url(CRATES_IO.parse().unwrap()); @@ -961,10 +962,10 @@ impl TomlManifest { let dep = replacement.to_dependency(spec.name(), cx, None)?; let dep = { - let version = spec.version().chain_error(|| { - human(format!("replacements must specify a version \ - to replace, but `{}` does not", - spec)) + let version = spec.version().ok_or_else(|| { + CargoError::from(format!("replacements must specify a version \ + to replace, but `{}` does not", + spec)) })?; let req = VersionReq::exact(version); dep.clone_inner().set_version_req(req) @@ -1205,10 +1206,10 @@ impl TomlTarget { match self.name { Some(ref name) => { if name.trim().is_empty() { - Err(human("library target names cannot be empty.".to_string())) + Err("library target names cannot be empty.".into()) } else if name.contains('-') { - Err(human(format!("library target names cannot contain hyphens: {}", - name))) + Err(format!("library target names cannot contain hyphens: {}", + name).into()) } else { Ok(()) } @@ -1221,12 +1222,12 @@ impl TomlTarget { match self.name { Some(ref name) => { if name.trim().is_empty() { - Err(human("binary target names cannot be empty.".to_string())) + Err("binary target names cannot be empty.".into()) } else { Ok(()) } }, - None => Err(human("binary target bin.name is required".to_string())) + None => Err("binary target bin.name is required".into()) } } @@ -1234,12 +1235,12 @@ impl TomlTarget { match self.name { Some(ref name) => { if name.trim().is_empty() { - Err(human("example target names cannot be empty".to_string())) + Err("example target names cannot be empty".into()) } else { Ok(()) } }, - None => Err(human("example target example.name is required".to_string())) + None => Err("example target example.name is required".into()) } } @@ -1247,12 +1248,12 @@ impl TomlTarget { match self.name { Some(ref name) => { if name.trim().is_empty() { - Err(human("test target names cannot be empty".to_string())) + Err("test target names cannot be empty".into()) } else { Ok(()) } }, - None => Err(human("test target test.name is required".to_string())) + None => Err("test target test.name is required".into()) } } @@ -1260,12 +1261,12 @@ impl TomlTarget { match self.name { Some(ref name) => { if name.trim().is_empty() { - Err(human("bench target names cannot be empty".to_string())) + Err("bench target names cannot be empty".into()) } else { Ok(()) } }, - None => Err(human("bench target bench.name is required".to_string())) + None => Err("bench target bench.name is required".into()) } } @@ -1280,7 +1281,7 @@ impl TomlTarget { // A plugin requires exporting plugin_registrar so a crate cannot be // both at once. if self.plugin == Some(true) && self.proc_macro() == Some(true) { - Err(human("lib.plugin and lib.proc-macro cannot both be true".to_string())) + Err("lib.plugin and lib.proc-macro cannot both be true".into()) } else { Ok(()) } diff --git a/src/crates-io/Cargo.toml b/src/crates-io/Cargo.toml index f7c52dcb611..b0daa85f2c9 100644 --- a/src/crates-io/Cargo.toml +++ b/src/crates-io/Cargo.toml @@ -14,6 +14,7 @@ path = "lib.rs" [dependencies] curl = "0.4" +error-chain = "0.10.0" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" diff --git a/src/crates-io/lib.rs b/src/crates-io/lib.rs index df032f349f2..05ca8e8de70 100644 --- a/src/crates-io/lib.rs +++ b/src/crates-io/lib.rs @@ -1,63 +1,72 @@ extern crate curl; extern crate url; +#[macro_use] +extern crate error_chain; extern crate serde_json; #[macro_use] extern crate serde_derive; use std::collections::HashMap; -use std::fmt; use std::fs::File; use std::io::prelude::*; use std::io::{self, Cursor}; -use std::result; use curl::easy::{Easy, List}; use url::percent_encoding::{percent_encode, QUERY_ENCODE_SET}; +error_chain! { + foreign_links { + Curl(curl::Error); + Io(io::Error); + Json(serde_json::Error); + } + + errors { + NotOkResponse(code: u32, headers: Vec, body: Vec){ + description("failed to get a 200 OK response") + display("failed to get a 200 OK response, got {} +headers: + {} +body: +{}", code, headers.join("\n ", ), String::from_utf8_lossy(body)) + } + NonUtf8Body { + description("response body was not utf-8") + display("response body was not utf-8") + } + Api(errs: Vec) { + display("api errors: {}", errs.join(", ")) + } + Unauthorized { + display("unauthorized API access") + } + TokenMissing{ + display("no upload token found, please run `cargo login`") + } + NotFound { + display("cannot find crate") + } + } + } + pub struct Registry { host: String, token: Option, handle: Easy, } -pub type Result = result::Result; - #[derive(PartialEq, Clone, Copy)] pub enum Auth { Authorized, - Unauthorized -} - -pub enum Error { - Curl(curl::Error), - NotOkResponse(u32, Vec, Vec), - NonUtf8Body, - Api(Vec), Unauthorized, - TokenMissing, - Io(io::Error), - NotFound, - Json(serde_json::Error), -} - -impl From for Error { - fn from(err: serde_json::Error) -> Error { - Error::Json(err) - } -} - -impl From for Error { - fn from(err: curl::Error) -> Error { - Error::Curl(err) - } } #[derive(Deserialize)] pub struct Crate { pub name: String, pub description: Option, - pub max_version: String + pub max_version: String, } #[derive(Serialize)] @@ -156,7 +165,7 @@ impl Registry { // (metadata for the package) // // - let stat = tarball.metadata().map_err(Error::Io)?; + let stat = tarball.metadata()?; let header = { let mut w = Vec::new(); w.extend([ @@ -181,7 +190,7 @@ impl Registry { let token = match self.token.as_ref() { Some(s) => s, - None => return Err(Error::TokenMissing), + None => return Err(Error::from_kind(ErrorKind::TokenMissing)), }; self.handle.put(true)?; self.handle.url(&url)?; @@ -191,9 +200,7 @@ impl Registry { headers.append(&format!("Authorization: {}", token))?; self.handle.http_headers(headers)?; - let body = handle(&mut self.handle, &mut |buf| { - body.read(buf).unwrap_or(0) - })?; + let body = handle(&mut self.handle, &mut |buf| body.read(buf).unwrap_or(0))?; let response = if body.len() > 0 { body.parse::()? @@ -201,23 +208,19 @@ impl Registry { "{}".parse()? }; - let invalid_categories: Vec = - response.get("warnings") - .and_then(|j| j.get("invalid_categories")) - .and_then(|j| j.as_array()) - .map(|x| { - x.iter().flat_map(|j| j.as_str()).map(Into::into).collect() - }) - .unwrap_or_else(Vec::new); - - let invalid_badges: Vec = - response.get("warnings") - .and_then(|j| j.get("invalid_badges")) - .and_then(|j| j.as_array()) - .map(|x| { - x.iter().flat_map(|j| j.as_str()).map(Into::into).collect() - }) - .unwrap_or_else(Vec::new); + let invalid_categories: Vec = response + .get("warnings") + .and_then(|j| j.get("invalid_categories")) + .and_then(|j| j.as_array()) + .map(|x| x.iter().flat_map(|j| j.as_str()).map(Into::into).collect()) + .unwrap_or_else(Vec::new); + + let invalid_badges: Vec = response + .get("warnings") + .and_then(|j| j.get("invalid_badges")) + .and_then(|j| j.as_array()) + .map(|x| x.iter().flat_map(|j| j.as_str()).map(Into::into).collect()) + .unwrap_or_else(Vec::new); Ok(Warnings { invalid_categories: invalid_categories, @@ -277,7 +280,7 @@ impl Registry { if authorized == Auth::Authorized { let token = match self.token.as_ref() { Some(s) => s, - None => return Err(Error::TokenMissing), + None => return Err(Error::from_kind(ErrorKind::TokenMissing)), }; headers.append(&format!("Authorization: {}", token))?; } @@ -314,48 +317,24 @@ fn handle(handle: &mut Easy, match handle.response_code()? { 0 => {} // file upload url sometimes 200 => {} - 403 => return Err(Error::Unauthorized), - 404 => return Err(Error::NotFound), - code => return Err(Error::NotOkResponse(code, headers, body)) + 403 => return Err(Error::from_kind(ErrorKind::Unauthorized)), + 404 => return Err(Error::from_kind(ErrorKind::NotFound)), + code => return Err(Error::from_kind(ErrorKind::NotOkResponse(code, headers, body))), } let body = match String::from_utf8(body) { Ok(body) => body, - Err(..) => return Err(Error::NonUtf8Body), + Err(..) => return Err(Error::from_kind(ErrorKind::NonUtf8Body)), }; match serde_json::from_str::(&body) { Ok(errors) => { - return Err(Error::Api(errors.errors.into_iter().map(|s| s.detail) - .collect())) + return Err(Error::from_kind(ErrorKind::Api(errors + .errors + .into_iter() + .map(|s| s.detail) + .collect()))) } Err(..) => {} } Ok(body) } - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::NonUtf8Body => write!(f, "response body was not utf-8"), - Error::Curl(ref err) => write!(f, "http error: {}", err), - Error::NotOkResponse(code, ref headers, ref body) => { - writeln!(f, "failed to get a 200 OK response, got {}", code)?; - writeln!(f, "headers:")?; - for header in headers { - writeln!(f, " {}", header)?; - } - writeln!(f, "body:")?; - writeln!(f, "{}", String::from_utf8_lossy(body))?; - Ok(()) - } - Error::Api(ref errs) => { - write!(f, "api errors: {}", errs.join(", ")) - } - Error::Unauthorized => write!(f, "unauthorized API access"), - Error::TokenMissing => write!(f, "no upload token found, please run `cargo login`"), - Error::Io(ref e) => write!(f, "io error: {}", e), - Error::NotFound => write!(f, "cannot find crate"), - Error::Json(ref e) => write!(f, "json error: {}", e), - } - } -} diff --git a/tests/cargotest/support/mod.rs b/tests/cargotest/support/mod.rs index 63b441b822b..8177785b25c 100644 --- a/tests/cargotest/support/mod.rs +++ b/tests/cargotest/support/mod.rs @@ -15,7 +15,7 @@ use serde_json::{self, Value}; use url::Url; use hamcrest as ham; use cargo::util::ProcessBuilder; -use cargo::util::ProcessError; +use cargo::util::{CargoError, CargoErrorKind, ProcessError}; use support::paths::CargoPathExt; @@ -719,7 +719,8 @@ impl<'a> ham::Matcher<&'a mut ProcessBuilder> for Execs { match res { Ok(out) => self.match_output(&out), - Err(ProcessError { output: Some(ref out), .. }) => { + Err(CargoError(CargoErrorKind::ProcessErrorKind( + ProcessError { output: Some(ref out), .. }), ..)) => { self.match_output(out) } Err(e) => { diff --git a/tests/doc.rs b/tests/doc.rs index ffc0e1421ca..119b5d32a44 100644 --- a/tests/doc.rs +++ b/tests/doc.rs @@ -1,5 +1,6 @@ extern crate cargotest; extern crate hamcrest; +extern crate cargo; use std::str; use std::fs; @@ -8,6 +9,7 @@ use cargotest::rustc_host; use cargotest::support::{project, execs, path2url}; use cargotest::support::registry::Package; use hamcrest::{assert_that, existing_file, existing_dir, is_not}; +use cargo::util::{CargoError, CargoErrorKind}; #[test] fn simple() { @@ -357,11 +359,16 @@ fn output_not_captured() { pub fn foo() {} "); - let output = p.cargo_process("doc").exec_with_output().err().unwrap() - .output.unwrap(); - let stderr = str::from_utf8(&output.stderr).unwrap(); - assert!(stderr.contains("☃"), "no snowman\n{}", stderr); - assert!(stderr.contains("unknown start of token"), "no message\n{}", stderr); + let error = p.cargo_process("doc").exec_with_output().err().unwrap(); + if let CargoError(CargoErrorKind::ProcessErrorKind(perr), ..) = error { + let output = perr.output.unwrap(); + let stderr = str::from_utf8(&output.stderr).unwrap(); + + assert!(stderr.contains("☃"), "no snowman\n{}", stderr); + assert!(stderr.contains("unknown start of token"), "no message{}", stderr); + } else { + assert!(false, "an error kind other than ProcessErrorKind was encountered"); + } } #[test]