Initial pass at boxed errors
authorYehuda Katz <wycats@gmail.com>
Thu, 19 Jun 2014 07:55:17 +0000 (00:55 -0700)
committerYehuda Katz <wycats@gmail.com>
Thu, 19 Jun 2014 07:57:12 +0000 (00:57 -0700)
The next step is to clean up the error handling in general so that
failure cases produce good errors.

23 files changed:
src/bin/cargo-compile.rs
src/bin/cargo-git-checkout.rs
src/bin/cargo-read-manifest.rs
src/bin/cargo.rs
src/cargo/core/manifest.rs
src/cargo/core/resolver.rs
src/cargo/core/version_req.rs
src/cargo/lib.rs
src/cargo/ops/cargo_compile.rs
src/cargo/ops/cargo_read_manifest.rs
src/cargo/ops/cargo_rustc.rs
src/cargo/sources/git/utils.rs
src/cargo/sources/path.rs
src/cargo/util/config.rs
src/cargo/util/errors.rs [new file with mode: 0644]
src/cargo/util/important_paths.rs
src/cargo/util/mod.rs
src/cargo/util/process_builder.rs
src/cargo/util/result.rs
src/cargo/util/toml.rs
tests/support/mod.rs
tests/test_cargo_compile.rs
tests/test_cargo_compile_git_deps.rs

index 1c02203835424d4a4decd0d76b38d13bf0292716..e327d7e7984d8273ba9813f0de6cce96389398ad 100644 (file)
@@ -10,10 +10,10 @@ extern crate log;
 
 use std::os;
 use hammer::FlagConfig;
-use cargo::{execute_main_without_stdin,CLIResult,CLIError,ToResult};
+use cargo::{execute_main_without_stdin};
 use cargo::ops;
+use cargo::util::{CliResult, CliError};
 use cargo::util::important_paths::find_project;
-use cargo::util::{ToCLI};
 
 #[deriving(PartialEq,Clone,Decodable,Encodable)]
 pub struct Options {
@@ -26,16 +26,16 @@ fn main() {
     execute_main_without_stdin(execute);
 }
 
-fn execute(options: Options) -> CLIResult<Option<()>> {
+fn execute(options: Options) -> CliResult<Option<()>> {
     debug!("executing; cmd=cargo-compile; args={}", os::args());
 
     let root = match options.manifest_path {
         Some(path) => Path::new(path),
         None => try!(find_project(os::getcwd(), "Cargo.toml")
                     .map(|path| path.join("Cargo.toml"))
-                    .to_result(|err|
-                        CLIError::new("Could not find Cargo.toml in this directory or any parent directory", Some(err), 102)))
+                    .map_err(|_|
+                        CliError::new("Could not find Cargo.toml in this directory or any parent directory", 102)))
     };
 
-    ops::compile(&root).map(|_| None).to_cli(101)
+    ops::compile(&root).map(|_| None).map_err(|err| CliError::from_boxed(err, 101))
 }
index 64ddc176da4a9cb069e00515f7409cc070cb823f..6f3f374250eca3180cc265cfb80161025c12e1a1 100644 (file)
@@ -6,10 +6,10 @@ extern crate hammer;
 extern crate url;
 
 use hammer::FlagConfig;
-use cargo::{execute_main_without_stdin,CLIResult,CLIError,ToResult};
+use cargo::{execute_main_without_stdin};
 use cargo::core::source::{Source,SourceId};
 use cargo::sources::git::{GitSource};
-use cargo::util::{Config,ToCLI};
+use cargo::util::{Config, CliResult, CliError, Require};
 use url::Url;
 
 #[deriving(PartialEq,Clone,Decodable)]
@@ -25,19 +25,18 @@ fn main() {
     execute_main_without_stdin(execute);
 }
 
-fn execute(options: Options) -> CLIResult<Option<()>> {
+fn execute(options: Options) -> CliResult<Option<()>> {
     let Options { url, reference, .. } = options;
 
-    let url: Url = try!(from_str(url.as_slice()).to_result(|_|
-        CLIError::new(format!("The URL `{}` you passed was not a valid URL", url), None::<&str>, 1)));
+    let url: Url = try!(from_str(url.as_slice())
+                        .require(|| format!("The URL `{}` you passed was not a valid URL", url))
+                        .map_err(|e| CliError::from_boxed(e, 1)));
 
     let source_id = SourceId::for_git(&url, reference.as_slice());
 
-    let mut source = GitSource::new(&source_id, &try!(Config::new().to_cli(1)));
+    let mut source = GitSource::new(&source_id, &try!(Config::new().map_err(|e| CliError::from_boxed(e, 1))));
 
-    try!(source.update().map_err(|e| {
-        CLIError::new(format!("Couldn't update {}: {}", source, e), None::<&str>, 1)
-    }));
+    try!(source.update().map_err(|e| CliError::new(format!("Couldn't update {}: {}", source, e), 1)));
 
     Ok(None)
 }
index 4d97627ce7af1a907400114e03103be8108eddf7..225c811d9c08a544d1d7161b2f2e32bd166f2b08 100644 (file)
@@ -5,8 +5,9 @@ extern crate serialize;
 extern crate hammer;
 
 use hammer::FlagConfig;
-use cargo::{execute_main_without_stdin,CLIResult,CLIError};
-use cargo::core::{Package,Source,SourceId};
+use cargo::{execute_main_without_stdin};
+use cargo::core::{Package, Source, SourceId};
+use cargo::util::{CliResult, CliError};
 use cargo::sources::{PathSource};
 
 #[deriving(PartialEq,Clone,Decodable)]
@@ -20,7 +21,7 @@ fn main() {
     execute_main_without_stdin(execute);
 }
 
-fn execute(options: Options) -> CLIResult<Option<Package>> {
+fn execute(options: Options) -> CliResult<Option<Package>> {
     let source_id = SourceId::for_path(&Path::new(options.manifest_path.as_slice()));
     let mut source = PathSource::new(&source_id);
 
@@ -29,5 +30,5 @@ fn execute(options: Options) -> CLIResult<Option<Package>> {
     source
         .get_root_package()
         .map(|pkg| Some(pkg))
-        .map_err(|err| CLIError::new(err.get_desc(), Some(err.get_detail()), 1))
+        .map_err(|err| CliError::from_boxed(err, 1))
 }
index 5293000e4ce676f7cc103d4dfb7efa343fb72fe4..2daaf3bef887442ed53886f0d23e520ff422033b 100644 (file)
@@ -12,9 +12,8 @@ use std::os;
 use std::io::process::{Command,InheritFd,ExitStatus,ExitSignal};
 use serialize::Encodable;
 use cargo::{NoFlags,execute_main_without_stdin,handle_error};
-use cargo::core::errors::{CLIError,CLIResult,ToResult};
 use cargo::util::important_paths::find_project;
-use cargo::util::{ToCLI,config,simple_human};
+use cargo::util::{CliError, CliResult, CargoResult, CargoError, Require, config, box_error};
 
 fn main() {
     execute();
@@ -58,17 +57,17 @@ fn execute() {
             .stderr(InheritFd(2))
             .status();
 
-        match command.map_err(|_| simple_human("No such subcommand")) {
+        match command {
             Ok(ExitStatus(0)) => (),
-            Ok(ExitStatus(i)) | Ok(ExitSignal(i)) => handle_error(simple_human("").to_cli(i as uint)),
-            Err(err) => handle_error(err.to_cli(127))
+            Ok(ExitStatus(i)) | Ok(ExitSignal(i)) => handle_error(CliError::new("", i as uint)),
+            Err(_) => handle_error(CliError::new("No such subcommand", 127))
         }
     }
 }
 
-fn process(args: Vec<String>) -> CLIResult<(String, Vec<String>)> {
+fn process(args: Vec<String>) -> CliResult<(String, Vec<String>)> {
     let args: Vec<String> = Vec::from_slice(args.tail());
-    let head = try!(args.iter().nth(0).to_result(|_| CLIError::new("No subcommand found", None::<&str>, 1))).to_str();
+    let head = try!(args.iter().nth(0).require(|| "No subcommand found").map_err(|err| CliError::from_boxed(err, 1))).to_str();
     let tail = Vec::from_slice(args.tail());
 
     Ok((head, tail))
@@ -91,9 +90,9 @@ impl FlagConfig for ConfigForKeyFlags {
     }
 }
 
-fn config_for_key(args: ConfigForKeyFlags) -> CLIResult<Option<ConfigOut>> {
-    let value = try!(config::get_config(os::getcwd(), args.key.as_slice()).to_result(|err|
-        CLIError::new("Couldn't load configuration", Some(err), 1)));
+fn config_for_key(args: ConfigForKeyFlags) -> CliResult<Option<ConfigOut>> {
+    let value = try!(config::get_config(os::getcwd(), args.key.as_slice()).map_err(|err|
+        CliError::new("Couldn't load configuration",  1)));
 
     if args.human {
         println!("{}", value);
@@ -116,9 +115,9 @@ impl FlagConfig for ConfigListFlags {
     }
 }
 
-fn config_list(args: ConfigListFlags) -> CLIResult<Option<ConfigOut>> {
-    let configs = try!(config::all_configs(os::getcwd()).to_result(|err|
-        CLIError::new("Couldn't load conifguration", Some(err), 1)));
+fn config_list(args: ConfigListFlags) -> CliResult<Option<ConfigOut>> {
+    let configs = try!(config::all_configs(os::getcwd()).map_err(|e|
+        CliError::new("Couldn't load configuration", 1)));
 
     if args.human {
         for (key, value) in configs.iter() {
@@ -130,12 +129,12 @@ fn config_list(args: ConfigListFlags) -> CLIResult<Option<ConfigOut>> {
     }
 }
 
-fn locate_project(_: NoFlags) -> CLIResult<Option<ProjectLocation>> {
-    let root = try!(find_project(os::getcwd(), "Cargo.toml").to_result(|err|
-        CLIError::new(err.to_str(), None::<&str>, 1)));
+fn locate_project(_: NoFlags) -> CliResult<Option<ProjectLocation>> {
+    let root = try!(find_project(os::getcwd(), "Cargo.toml").map_err(|e| CliError::from_boxed(e, 1)));
 
-    let string = try!(root.as_str().to_result(|_|
-        CLIError::new(format!("Your project path contains characters not representable in Unicode: {}", os::getcwd().display()), None::<&str>, 1)));
+    let string = try!(root.as_str()
+                      .require(|| "Your project path contains characters not representable in Unicode")
+                      .map_err(|e| CliError::from_boxed(e, 1)));
 
     Ok(Some(ProjectLocation { root: string.to_str() }))
 }
index b8d8b48ec3485176bc4d0e7e7e8fcc6e4562bce5..da904e174d9aecf5568752967eded6475d37bb2b 100644 (file)
@@ -10,7 +10,7 @@ use core::{
     Summary
 };
 use core::dependency::SerializedDependency;
-use util::{CargoResult,simple_human};
+use util::{CargoResult, CargoError, box_error};
 
 #[deriving(PartialEq,Clone)]
 pub struct Manifest {
@@ -65,7 +65,7 @@ impl LibKind {
             "rlib" => Ok(Rlib),
             "dylib" => Ok(Dylib),
             "staticlib" => Ok(StaticLib),
-            _ => Err(simple_human(format!("{} was not one of lib|rlib|dylib|staticlib", string)))
+            _ => Err(box_error(format!("{} was not one of lib|rlib|dylib|staticlib", string)))
         }
     }
 
index 23eac0b6bb2cf7c7b50990695056acb69f36d029..3c6bcf93bdc61dd896456739ccc8450f9c27658b 100644 (file)
@@ -1,11 +1,12 @@
 use std::collections::HashMap;
+
 use core::{
     Dependency,
     PackageId,
     Summary,
     Registry
 };
-use util::result::CargoResult;
+use util::errors::CargoResult;
 
 /* TODO:
  * - The correct input here is not a registry. Resolves should be performable
index 1b4d7bcf77769bd90423647f778b935e6fc19a30..065f26bbe6bd2d417e7e6d5a0bc19eabc465f6f2 100644 (file)
@@ -1,7 +1,7 @@
 use std::fmt;
 use std::str::CharOffsets;
 use semver::Version;
-use util::{other_error,CargoResult};
+use util::{CargoResult, error};
 
 #[deriving(PartialEq,Clone)]
 pub struct VersionReq {
@@ -49,7 +49,7 @@ impl VersionReq {
         }
 
         if lexer.is_error() {
-            return Err(other_error("invalid version requirement"));
+            return Err(error("invalid version requirement"));
         }
 
         predicates.push(try!(builder.build()));
@@ -151,12 +151,12 @@ impl PredBuilder {
 
     fn set_sigil(&mut self, sigil: &str) -> CargoResult<()> {
         if self.op.is_some() {
-            return Err(other_error("op already set"));
+            return Err(error("op already set"));
         }
 
         match Op::from_sigil(sigil) {
             Some(op) => self.op = Some(op),
-            _ => return Err(other_error("invalid sigil"))
+            _ => return Err(error("invalid sigil"))
         }
 
         Ok(())
@@ -188,12 +188,12 @@ impl PredBuilder {
     fn build(&self) -> CargoResult<Predicate> {
         let op = match self.op {
             Some(x) => x,
-            None => return Err(other_error("op required"))
+            None => return Err(error("op required"))
         };
 
         let major = match self.major {
             Some(x) => x,
-            None => return Err(other_error("major version required"))
+            None => return Err(error("major version required"))
         };
 
         Ok(Predicate {
@@ -384,7 +384,7 @@ fn parse_version_part(s: &str) -> CargoResult<uint> {
         let n = (c as uint) - ('0' as uint);
 
         if n > 9 {
-            return Err(other_error("version components must be numeric"));
+            return Err(error("version components must be numeric"));
         }
 
         ret *= 10;
index ed65a4583c3926d1552c8ef5b07d8785da344415..c8dd26ef2c45ae087ea4a30c34de9c360448794f 100644 (file)
@@ -20,7 +20,7 @@ extern crate hamcrest;
 use serialize::{Decoder,Encoder,Decodable,Encodable,json};
 use std::io;
 use hammer::{FlagDecoder,FlagConfig,HammerError};
-pub use core::errors::{CLIError,CLIResult,ToResult};
+pub use util::{CliError, CliResult};
 
 macro_rules! some(
   ($e:expr) => (
@@ -46,8 +46,8 @@ pub struct NoFlags;
 
 impl FlagConfig for NoFlags {}
 
-pub fn execute_main<'a, T: RepresentsFlags, U: RepresentsJSON, V: Encodable<json::Encoder<'a>, io::IoError>>(exec: fn(T, U) -> CLIResult<Option<V>>) {
-    fn call<'a, T: RepresentsFlags, U: RepresentsJSON, V: Encodable<json::Encoder<'a>, io::IoError>>(exec: fn(T, U) -> CLIResult<Option<V>>) -> CLIResult<Option<V>> {
+pub fn execute_main<'a, T: RepresentsFlags, U: RepresentsJSON, V: Encodable<json::Encoder<'a>, io::IoError>>(exec: fn(T, U) -> CliResult<Option<V>>) {
+    fn call<'a, T: RepresentsFlags, U: RepresentsJSON, V: Encodable<json::Encoder<'a>, io::IoError>>(exec: fn(T, U) -> CliResult<Option<V>>) -> CliResult<Option<V>> {
         let flags = try!(flags_from_args::<T>());
         let json = try!(json_from_stdin::<U>());
 
@@ -57,8 +57,8 @@ pub fn execute_main<'a, T: RepresentsFlags, U: RepresentsJSON, V: Encodable<json
     process_executed(call(exec))
 }
 
-pub fn execute_main_without_stdin<'a, T: RepresentsFlags, V: Encodable<json::Encoder<'a>, io::IoError>>(exec: fn(T) -> CLIResult<Option<V>>) {
-    fn call<'a, T: RepresentsFlags, V: Encodable<json::Encoder<'a>, io::IoError>>(exec: fn(T) -> CLIResult<Option<V>>) -> CLIResult<Option<V>> {
+pub fn execute_main_without_stdin<'a, T: RepresentsFlags, V: Encodable<json::Encoder<'a>, io::IoError>>(exec: fn(T) -> CliResult<Option<V>>) {
+    fn call<'a, T: RepresentsFlags, V: Encodable<json::Encoder<'a>, io::IoError>>(exec: fn(T) -> CliResult<Option<V>>) -> CliResult<Option<V>> {
         let flags = try!(flags_from_args::<T>());
 
         exec(flags)
@@ -67,7 +67,7 @@ pub fn execute_main_without_stdin<'a, T: RepresentsFlags, V: Encodable<json::Enc
     process_executed(call(exec));
 }
 
-pub fn process_executed<'a, T: Encodable<json::Encoder<'a>, io::IoError>>(result: CLIResult<Option<T>>) {
+pub fn process_executed<'a, T: Encodable<json::Encoder<'a>, io::IoError>>(result: CliResult<Option<T>>) {
     match result {
         Err(e) => handle_error(e),
         Ok(encodable) => {
@@ -79,11 +79,12 @@ pub fn process_executed<'a, T: Encodable<json::Encoder<'a>, io::IoError>>(result
     }
 }
 
-pub fn handle_error(err: CLIError) {
+pub fn handle_error(err: CliError) {
     log!(4, "handle_error; err={}", err);
 
-    let CLIError { msg, exit_code, .. } = err;
-    let _ = write!(&mut std::io::stderr(), "{}", msg);
+    let CliError { error, exit_code, .. } = err;
+    let _ = write!(&mut std::io::stderr(), "{}", error);
+    // TODO: Cause chains
     //detail.map(|d| write!(&mut std::io::stderr(), ":\n{}", d));
 
     std::os::set_exit_status(exit_code as int);
@@ -93,17 +94,17 @@ fn args() -> Vec<String> {
     std::os::args()
 }
 
-fn flags_from_args<T: RepresentsFlags>() -> CLIResult<T> {
+fn flags_from_args<T: RepresentsFlags>() -> CliResult<T> {
     let mut decoder = FlagDecoder::new::<T>(args().tail());
-    Decodable::decode(&mut decoder).to_result(|e: HammerError| CLIError::new(e.message, None::<&str>, 1))
+    Decodable::decode(&mut decoder).map_err(|e: HammerError| CliError::new(e.message, 1))
 }
 
-fn json_from_stdin<T: RepresentsJSON>() -> CLIResult<T> {
+fn json_from_stdin<T: RepresentsJSON>() -> CliResult<T> {
     let mut reader = io::stdin();
-    let input = try!(reader.read_to_str().to_result(|_| CLIError::new("Standard in did not exist or was not UTF-8", None::<&str>, 1)));
+    let input = try!(reader.read_to_str().map_err(|e| CliError::new("Standard in did not exist or was not UTF-8", 1)));
 
-    let json = try!(json::from_str(input.as_slice()).to_result(|_| CLIError::new("Could not parse standard in as JSON", Some(input.clone()), 1)));
+    let json = try!(json::from_str(input.as_slice()).map_err(|e| CliError::new("Could not parse standard in as JSON", 1)));
     let mut decoder = json::Decoder::new(json);
 
-    Decodable::decode(&mut decoder).to_result(|e: json::DecoderError| CLIError::new("Could not process standard in as input", Some(e), 1))
+    Decodable::decode(&mut decoder).map_err(|e: json::DecoderError| CliError::new("Could not process standard in as input", 1))
 }
index dafa492bd4b56826579b2c09e024a789a7abdaca..1ca4e2eeed51b1c4d43b22c4338284a00db5225f 100644 (file)
@@ -20,7 +20,7 @@ use core::{Source,SourceId,PackageSet,resolver};
 use core::registry::PackageRegistry;
 use ops;
 use sources::{PathSource};
-use util::{CargoResult,Wrap,config,other_error};
+use util::{CargoResult, Wrap, config, error};
 
 pub fn compile(manifest_path: &Path) -> CargoResult<()> {
     log!(4, "compile; manifest-path={}", manifest_path.display());
@@ -56,7 +56,7 @@ fn source_ids_from_config() -> CargoResult<Vec<SourceId>> {
     let config_paths = configs.find_equiv(&"paths").map(|v| v.clone()).unwrap_or_else(|| ConfigValue::new());
 
     let paths: Vec<Path> = match config_paths.get_value() {
-        &config::String(_) => return Err(other_error("The path was configured as a String instead of a List")),
+        &config::String(_) => return Err(error("The path was configured as a String instead of a List")),
         &config::List(ref list) => list.iter().map(|path| Path::new(path.as_slice())).collect()
     };
 
index c1f723bd342054b17438206887a2770b9d55a17e..809c38a2015de54fe8338cdfb4a3f8bc8941d531 100644 (file)
@@ -1,16 +1,16 @@
 use std::io::File;
 use util;
 use core::{Package,Manifest,SourceId};
-use util::{CargoResult,io_error};
+use util::{CargoResult, CargoError, box_error, human};
 
 pub fn read_manifest(contents: &[u8], source_id: &SourceId) -> CargoResult<(Manifest, Vec<Path>)> {
-    util::toml::to_manifest(contents, source_id)
+    util::toml::to_manifest(contents, source_id).map_err(human)
 }
 
 pub fn read_package(path: &Path, source_id: &SourceId) -> CargoResult<(Package, Vec<Path>)> {
     log!(5, "read_package; path={}; source-id={}", path.display(), source_id);
-    let mut file = try!(File::open(path).map_err(io_error));
-    let data = try!(file.read_to_end().map_err(io_error));
+    let mut file = try!(File::open(path).map_err(box_error));
+    let data = try!(file.read_to_end().map_err(box_error));
     let (manifest, nested) = try!(read_manifest(data.as_slice(), source_id));
 
     Ok((Package::new(manifest, path), nested))
index 2d38610c5a8f252b2cfa0286336080c867fae07b..90cb85f8a5dfb196735409d7bbc7f2b3f59d1d48 100644 (file)
@@ -4,8 +4,7 @@ use std::path::Path;
 use std::str;
 use core::{Package,PackageSet,Target};
 use util;
-use util::{other_error,human_error,CargoResult,CargoError,ProcessBuilder};
-use util::result::ProcessError;
+use util::{CargoResult, CargoError, ProcessBuilder, error, human, box_error};
 
 type Args = Vec<String>;
 
@@ -48,7 +47,7 @@ fn compile_pkg(pkg: &Package, dest: &Path, deps_dir: &Path, primary: bool) -> Ca
 
 fn mk_target(target: &Path) -> CargoResult<()> {
     io::fs::mkdir_recursive(target, io::UserRWX)
-      .map_err(|_| other_error("could not create target directory"))
+      .map_err(|_| error("could not create target directory"))
 }
 
 fn rustc(root: &Path, target: &Target, dest: &Path, deps: &Path, verbose: bool) -> CargoResult<()> {
@@ -62,10 +61,16 @@ fn rustc(root: &Path, target: &Target, dest: &Path, deps: &Path, verbose: bool)
         let rustc = prepare_rustc(root, target, *crate_type, dest, deps);
 
         try!((if verbose {
-            rustc.exec()
+            rustc.exec().map_err(|err| {
+                log!(5, "exec failed; error={}", err.description());
+                human(err)
+            })
         } else {
-            rustc.exec_with_output().and(Ok(()))
-        }).map_err(|e| rustc_to_cargo_err(rustc.get_args().as_slice(), root, e)));
+            rustc.exec_with_output().and(Ok(())).map_err(|err| {
+                log!(5, "exec_with_output failed; error={}", err.description());
+                human(err)
+            })
+        }));
     }
 
     Ok(())
@@ -97,29 +102,9 @@ fn build_deps_args(dst: &mut Args, deps: &Path) {
     dst.push(deps.display().to_str());
 }
 
-fn rustc_to_cargo_err(args: &[String], cwd: &Path, err: CargoError) -> CargoError {
-    let msg = {
-        let output = match err {
-            CargoError { kind: ProcessError(_, ref output), .. } => output,
-            _ => fail!("Bug! exec() returned an error other than a ProcessError")
-        };
-
-        let mut msg = format!("failed to execute: `rustc {}`", args.connect(" "));
-
-        output.as_ref().map(|o| {
-            let second = format!("; Error:\n{}", str::from_utf8_lossy(o.error.as_slice()));
-            msg.push_str(second.as_slice());
-        });
-
-        msg
-    };
-
-    human_error(msg, format!("root={}", cwd.display()), err)
-}
-
 fn topsort(deps: &PackageSet) -> CargoResult<PackageSet> {
     match deps.sort() {
         Some(deps) => Ok(deps),
-        None => return Err(other_error("circular dependency detected"))
+        None => return Err(error("circular dependency detected"))
     }
 }
index 8cccdb5845e4d504786189041d7a2dd28469ccea..f8f9f59549fadc90de8beef9e811e0ffc71bc612 100644 (file)
@@ -1,5 +1,5 @@
 use url::Url;
-use util::{CargoResult,ProcessBuilder,io_error,human_error,process};
+use util::{CargoResult, ProcessBuilder, process, box_error};
 use std::fmt;
 use std::fmt::{Show,Formatter};
 use std::str;
@@ -173,8 +173,7 @@ impl GitRemote {
     fn clone_into(&self, path: &Path) -> CargoResult<()> {
         let dirname = Path::new(path.dirname());
 
-        try!(mkdir_recursive(path, UserDir).map_err(|err|
-            human_error(format!("Couldn't recursively create `{}`", dirname.display()), format!("path={}", dirname.display()), io_error(err))));
+        try!(mkdir_recursive(path, UserDir).map_err(box_error));
 
         Ok(git!(dirname, "clone {} {} --bare --no-hardlinks --quiet", self.fetch_location(), path.display()))
     }
@@ -228,15 +227,15 @@ impl GitCheckout {
         let dirname = Path::new(self.location.dirname());
 
         try!(mkdir_recursive(&dirname, UserDir).map_err(|e|
-            human_error(format!("Couldn't mkdir {}", Path::new(self.location.dirname()).display()), None::<&str>, io_error(e))));
+            box_error(format!("Couldn't mkdir {}", Path::new(self.location.dirname()).display()))));
 
         if self.location.exists() {
             try!(rmdir_recursive(&self.location).map_err(|e|
-                human_error(format!("Couldn't rmdir {}", Path::new(&self.location).display()), None::<&str>, io_error(e))));
+                box_error(format!("Couldn't rmdir {}", Path::new(&self.location).display()))));
         }
 
         git!(dirname, "clone --no-checkout --quiet {} {}", self.get_source().display(), self.location.display());
-        try!(chmod(&self.location, AllPermissions).map_err(io_error));
+        try!(chmod(&self.location, AllPermissions).map_err(box_error));
 
         Ok(())
     }
@@ -264,12 +263,12 @@ fn git(path: &Path, str: &str) -> ProcessBuilder {
 
 fn git_inherit(path: &Path, str: String) -> CargoResult<()> {
     git(path, str.as_slice()).exec().map_err(|err|
-        human_error(format!("Executing `git {}` failed: {}", str, err), None::<&str>, err))
+        box_error(format!("Executing `git {}` failed: {}", str, err)))
 }
 
 fn git_output(path: &Path, str: String) -> CargoResult<String> {
     let output = try!(git(path, str.as_slice()).exec_with_output().map_err(|err|
-        human_error(format!("Executing `git {}` failed", str), None::<&str>, err)));
+        box_error(format!("Executing `git {}` failed", str))));
 
     Ok(to_str(output.output.as_slice()).as_slice().trim_right().to_str())
 }
index c8e9d6596c06b8dacda9cfb434599b3358a4df88..918027469e0c320f5066ec868648dd27c39c242f 100644 (file)
@@ -2,7 +2,7 @@ use std::fmt;
 use std::fmt::{Show,Formatter};
 use core::{Package,PackageId,Summary,SourceId,Source};
 use ops;
-use util::{CargoResult,simple_human};
+use util::{CargoResult, box_error};
 
 pub struct PathSource {
     id: SourceId,
@@ -45,7 +45,7 @@ impl PathSource {
 
         match self.packages.as_slice().head() {
             Some(pkg) => Ok(pkg.clone()),
-            None => Err(simple_human("no package found in source"))
+            None => Err(box_error("no package found in source"))
         }
     }
 }
index ab1126ea28659d65fdb3fc575eea8de74ce03bbc..535b87fe78fc5a5284b2785f5b361ca579e75d83 100644 (file)
@@ -2,7 +2,7 @@ use std::{io,fmt,os};
 use std::collections::HashMap;
 use serialize::{Encodable,Encoder};
 use toml;
-use util::{CargoResult,Require,other_error,simple_human};
+use util::{CargoResult, Require, error, internal_error};
 
 pub struct Config {
     home_path: Path
@@ -12,7 +12,7 @@ impl Config {
     pub fn new() -> CargoResult<Config> {
         Ok(Config {
             home_path: try!(os::homedir()
-                            .require(simple_human("Couldn't find the home directory")))
+                            .require(|| "Couldn't find the home directory"))
         })
     }
 
@@ -96,7 +96,7 @@ impl fmt::Show for ConfigValue {
 
 pub fn get_config(pwd: Path, key: &str) -> CargoResult<ConfigValue> {
     find_in_tree(&pwd, |file| extract_config(file, key))
-        .map_err(|_| other_error("config key not found").with_detail(format!("key={}", key)))
+        .map_err(|_| internal_error("config key not found", format!("key={}", key)))
 }
 
 pub fn all_configs(pwd: Path) -> CargoResult<HashMap<String, ConfigValue>> {
@@ -115,7 +115,7 @@ fn find_in_tree<T>(pwd: &Path, walk: |io::fs::File| -> CargoResult<T>) -> CargoR
     loop {
         let possible = current.join(".cargo").join("config");
         if possible.exists() {
-            let file = try!(io::fs::File::open(&possible).map_err(|_| other_error("could not open file")));
+            let file = try!(io::fs::File::open(&possible).map_err(|_| error("could not open file")));
             match walk(file) {
                 Ok(res) => return Ok(res),
                 _ => ()
@@ -125,7 +125,7 @@ fn find_in_tree<T>(pwd: &Path, walk: |io::fs::File| -> CargoResult<T>) -> CargoR
         if !current.pop() { break; }
     }
 
-    Err(other_error(""))
+    Err(error(""))
 }
 
 fn walk_tree(pwd: &Path, walk: |io::fs::File| -> CargoResult<()>) -> CargoResult<()> {
@@ -135,14 +135,14 @@ fn walk_tree(pwd: &Path, walk: |io::fs::File| -> CargoResult<()>) -> CargoResult
     loop {
         let possible = current.join(".cargo").join("config");
         if possible.exists() {
-            let file = try!(io::fs::File::open(&possible).map_err(|_| other_error("could not open file")));
+            let file = try!(io::fs::File::open(&possible).map_err(|_| error("could not open file")));
             match walk(file) {
                 Err(_) => err = false,
                 _ => ()
             }
         }
 
-        if err { return Err(other_error("")); }
+        if err { return Err(error("")); }
         if !current.pop() { break; }
     }
 
@@ -152,13 +152,13 @@ fn walk_tree(pwd: &Path, walk: |io::fs::File| -> CargoResult<()>) -> CargoResult
 fn extract_config(file: io::fs::File, key: &str) -> CargoResult<ConfigValue> {
     let path = file.path().clone();
     let mut buf = io::BufferedReader::new(file);
-    let root = try!(toml::parse_from_buffer(&mut buf).map_err(|_| other_error("")));
-    let val = try!(root.lookup(key).require(other_error("")));
+    let root = try!(toml::parse_from_buffer(&mut buf).map_err(|_| error("")));
+    let val = try!(root.lookup(key).require(|| error("")));
 
     let v = match val {
         &toml::String(ref val) => String(val.clone()),
         &toml::Array(ref val) => List(val.iter().map(|s: &toml::Value| s.to_str()).collect()),
-        _ => return Err(other_error(""))
+        _ => return Err(error(""))
     };
 
     Ok(ConfigValue{ value: v, path: vec!(path) })
@@ -168,10 +168,10 @@ fn extract_all_configs(file: io::fs::File, map: &mut HashMap<String, ConfigValue
     let path = file.path().clone();
     let mut buf = io::BufferedReader::new(file);
     let root = try!(toml::parse_from_buffer(&mut buf).map_err(|err|
-        other_error("could not parse Toml manifest").with_detail(format!("path={}; err={}", path.display(), err.to_str()))));
+        internal_error("could not parse Toml manifest", format!("path={}; err={}", path.display(), err.to_str()))));
 
     let table = try!(root.get_table()
-        .require(other_error("could not parse Toml manifest").with_detail(format!("path={}", path.display()))));
+        .require(|| internal_error("could not parse Toml manifest", format!("path={}", path.display()))));
 
     for (key, value) in table.iter() {
         match value {
@@ -182,7 +182,7 @@ fn extract_all_configs(file: io::fs::File, map: &mut HashMap<String, ConfigValue
                 });
 
                 try!(merge_array(config, val.as_slice(), &path).map_err(|err|
-                    other_error("missing").with_detail(format!("The `{}` key in your config {}", key, err))));
+                    error(format!("The `{}` key in your config {}", key, err))));
             },
             _ => ()
         }
@@ -193,11 +193,11 @@ fn extract_all_configs(file: io::fs::File, map: &mut HashMap<String, ConfigValue
 
 fn merge_array(existing: &mut ConfigValue, val: &[toml::Value], path: &Path) -> CargoResult<()> {
     match existing.value {
-        String(_) => return Err(other_error("should be an Array, but it was a String")),
+        String(_) => return Err(error("should be an Array, but it was a String")),
         List(ref mut list) => {
             let new_list: Vec<CargoResult<String>> = val.iter().map(|s: &toml::Value| toml_string(s)).collect();
             if new_list.iter().any(|v| v.is_err()) {
-                return Err(other_error("should be an Array of Strings, but was an Array of other values"));
+                return Err(error("should be an Array of Strings, but was an Array of other values"));
             } else {
                 let new_list: Vec<String> = new_list.move_iter().map(|v| v.unwrap()).collect();
                 list.push_all(new_list.as_slice());
@@ -211,6 +211,6 @@ fn merge_array(existing: &mut ConfigValue, val: &[toml::Value], path: &Path) ->
 fn toml_string(val: &toml::Value) -> CargoResult<String> {
     match val {
         &toml::String(ref str) => Ok(str.clone()),
-        _ => Err(other_error(""))
+        _ => Err(error(""))
     }
 }
diff --git a/src/cargo/util/errors.rs b/src/cargo/util/errors.rs
new file mode 100644 (file)
index 0000000..833cd01
--- /dev/null
@@ -0,0 +1,203 @@
+use std::io::process::{Command,ProcessOutput,ProcessExit,ExitStatus,ExitSignal};
+use std::io::IoError;
+use std::fmt;
+use std::fmt::{Show, Formatter};
+
+use TomlError = toml::Error;
+
+pub trait CargoError {
+    fn description(&self) -> String;
+    fn detail(&self) -> Option<String> { None }
+    fn cause<'a>(&'a self) -> Option<&'a CargoError> { None }
+    fn is_human(&self) -> bool { false }
+
+    fn concrete(&self) -> ConcreteCargoError {
+        ConcreteCargoError {
+            description: self.description(),
+            detail: self.detail(),
+            cause: self.cause().map(|c| box c.concrete() as Box<CargoError>),
+            is_human: self.is_human()
+        }
+    }
+}
+
+impl Show for Box<CargoError> {
+    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+        try!(write!(f, "{}", self.description()));
+        Ok(())
+    }
+}
+
+impl CargoError for Box<CargoError> {
+    fn description(&self) -> String {
+        (*self).description()
+    }
+
+    fn detail(&self) -> Option<String> {
+        (*self).detail()
+    }
+
+    fn cause<'a>(&'a self) -> Option<&'a CargoError> {
+        (*self).cause()
+    }
+
+    fn is_human(&self) -> bool {
+        (*self).is_human()
+    }
+}
+
+pub type CargoResult<T> = Result<T, Box<CargoError>>;
+
+impl CargoError for &'static str {
+    fn description(&self) -> String { self.to_str() }
+    fn is_human(&self) -> bool { true }
+}
+
+impl CargoError for String {
+    fn description(&self) -> String { self.to_str() }
+    fn is_human(&self) -> bool { true }
+}
+
+impl CargoError for IoError {
+    fn description(&self) -> String { self.to_str() }
+}
+
+impl CargoError for TomlError {
+    fn description(&self) -> String { self.to_str() }
+}
+
+pub struct ProcessError {
+    pub command: String,
+    pub exit: Option<ProcessExit>,
+    pub output: Option<ProcessOutput>,
+    pub detail: Option<String>,
+    pub cause: Option<Box<CargoError>>
+}
+
+impl Show for ProcessError {
+    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+        let exit = match self.exit {
+            Some(ExitStatus(i)) | Some(ExitSignal(i)) => i.to_str(),
+            None => "never executed".to_str()
+        };
+        write!(f, "process failed: `{}` (status={})", self.command, exit)
+    }
+}
+
+impl CargoError for ProcessError {
+    fn description(&self) -> String {
+        let exit = match self.exit {
+            Some(ExitStatus(i)) | Some(ExitSignal(i)) => i.to_str(),
+            None => "never executed".to_str()
+        };
+        format!("Executing `{}` failed (status={})", self.command, exit)
+    }
+
+    fn detail(&self) -> Option<String> {
+        self.detail.clone()
+    }
+
+    fn cause<'a>(&'a self) -> Option<&'a CargoError> {
+        self.cause.as_ref().map(|c| { let err: &CargoError = *c; err })
+    }
+}
+
+struct ConcreteCargoError {
+    description: String,
+    detail: Option<String>,
+    cause: Option<Box<CargoError>>,
+    is_human: bool
+}
+
+impl Show for ConcreteCargoError {
+    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+        write!(f, "{}", self.description)
+    }
+}
+
+impl CargoError for ConcreteCargoError {
+    fn description(&self) -> String {
+        self.description.clone()
+    }
+
+    fn detail(&self) -> Option<String> {
+        self.detail.clone()
+    }
+
+    fn cause<'a>(&'a self) -> Option<&'a CargoError> {
+        self.cause.as_ref().map(|c| { let err: &CargoError = *c; err })
+    }
+
+    fn is_human(&self) -> bool {
+        self.is_human
+    }
+}
+
+pub type CliResult<T> = Result<T, CliError>;
+
+#[deriving(Show)]
+pub struct CliError {
+    pub error: Box<CargoError>,
+    pub exit_code: uint
+}
+
+impl CliError {
+    pub fn new<E: CargoError + 'static>(error: E, code: uint) -> CliError {
+        let error = box error as Box<CargoError>;
+        CliError::from_boxed(error, code)
+    }
+
+    pub fn from_boxed(error: Box<CargoError>, code: uint) -> CliError {
+        let error = if error.is_human() {
+            error
+        } else {
+            chain(error, "An unknown error occurred")
+        };
+
+        CliError { error: error, exit_code: code }
+    }
+}
+
+pub fn process_error<S: Str>(msg: S, command: &Command, status: Option<&ProcessExit>, output: Option<&ProcessOutput>) -> ProcessError {
+    ProcessError {
+        command: command.to_str(),
+        exit: status.map(|o| o.clone()),
+        output: output.map(|o| o.clone()),
+        detail: None,
+        cause: None
+    }
+}
+
+pub fn internal_error<S1: Str, S2: Str>(error: S1, detail: S2) -> Box<CargoError> {
+    box ConcreteCargoError {
+        description: error.as_slice().to_str(),
+        detail: Some(detail.as_slice().to_str()),
+        cause: None,
+        is_human: false
+    } as Box<CargoError>
+}
+
+pub fn error<S1: Str>(error: S1) -> Box<CargoError> {
+    box ConcreteCargoError {
+        description: error.as_slice().to_str(),
+        detail: None,
+        cause: None,
+        is_human: false
+    } as Box<CargoError>
+}
+
+pub fn human<E: CargoError>(error: E) -> Box<CargoError> {
+    let mut concrete = error.concrete();
+    concrete.is_human = true;
+    box concrete as Box<CargoError>
+}
+
+pub fn chain<E: CargoError>(original: Box<CargoError>, update: E) -> Box<CargoError> {
+    let mut concrete = update.concrete();
+    concrete.cause = Some(original);
+    box concrete as Box<CargoError>
+}
+
+pub fn box_error<S: CargoError + 'static>(err: S) -> Box<CargoError> {
+    box err as Box<CargoError>
+}
index 8238309dca4d40742fb4280706836b9a6355b6de..f8fd25981625b146f1b356a18fd085734dffe514 100644 (file)
@@ -1,4 +1,4 @@
-use util::{other_error,CargoResult,CargoError};
+use util::{CargoResult, CargoError, internal_error};
 
 pub fn find_project(pwd: Path, file: &str) -> CargoResult<Path> {
     let mut current = pwd.clone();
@@ -14,7 +14,7 @@ pub fn find_project(pwd: Path, file: &str) -> CargoResult<Path> {
     Err(manifest_missing_err(&pwd, file.as_slice()))
 }
 
-fn manifest_missing_err(pwd: &Path, file: &str) -> CargoError {
-    other_error("manifest not found")
-        .with_detail(format!("pwd={}; file={}", pwd.display(), file))
+fn manifest_missing_err(pwd: &Path, file: &str) -> Box<CargoError> {
+    internal_error("manifest not found",
+                   format!("pwd={}; file={}", pwd.display(), file))
 }
index 848d94cc9174ec24ba58f8790ac1e6d10db93e75..8957948c991cb0822ddcc7f865ad571152ba1b36 100644 (file)
@@ -1,6 +1,8 @@
 pub use self::config::Config;
 pub use self::process_builder::{process,ProcessBuilder};
-pub use self::result::{CargoError,CargoResult,Wrap,Require,ToCLI,other_error,human_error,simple_human,toml_error,io_error,process_error};
+pub use self::result::{Wrap, Require};
+pub use self::errors::{CargoResult, CargoError, CliResult, CliError, ProcessError};
+pub use self::errors::{process_error, box_error, internal_error, error, human, chain};
 pub use self::paths::realpath;
 
 pub mod graph;
@@ -10,3 +12,4 @@ pub mod important_paths;
 pub mod result;
 pub mod toml;
 pub mod paths;
+pub mod errors;
index 6d26eea2647c3d74b2b3e53afcbe916d731f2755..6adede8eb222067105de7651632f52708d4e18e4 100644 (file)
@@ -3,7 +3,7 @@ use std::fmt::{Show,Formatter};
 use std::os;
 use std::path::Path;
 use std::io::process::{Command,ProcessOutput,InheritFd};
-use util::{CargoResult,io_error,process_error};
+use util::{CargoResult, CargoError, ProcessError, process_error, box_error};
 use std::collections::HashMap;
 
 #[deriving(Clone,PartialEq)]
@@ -65,34 +65,36 @@ impl ProcessBuilder {
     }
 
     // TODO: should InheritFd be hardcoded?
-    pub fn exec(&self) -> CargoResult<()> {
+    pub fn exec(&self) -> Result<(), ProcessError> {
         let mut command = self.build_command();
         command
             .env(self.build_env().as_slice())
             .stdout(InheritFd(1))
             .stderr(InheritFd(2));
 
-        let exit = try!(command.status().map_err(io_error));
+        let msg = || format!("Could not execute process `{}`", self.debug_string());
+
+        let exit = try!(command.status().map_err(|e| process_error(msg(), &command, None, None)));
 
         if exit.success() {
             Ok(())
         } else {
-            let msg = format!("Could not execute process `{}`", self.debug_string());
-            Err(process_error(msg, exit, None))
+            Err(process_error(msg(), &command, Some(&exit), None))
         }
     }
 
-    pub fn exec_with_output(&self) -> CargoResult<ProcessOutput> {
+    pub fn exec_with_output(&self) -> Result<ProcessOutput, ProcessError> {
         let mut command = self.build_command();
         command.env(self.build_env().as_slice());
 
-        let output = try!(command.output().map_err(io_error));
+        let msg = || format!("Could not execute process `{}`", self.debug_string());
+
+        let output = try!(command.output().map_err(|e| process_error(msg(), &command, None, None)));
 
         if output.status.success() {
             Ok(output)
         } else {
-            let msg = format!("Could not execute process `{}`", self.debug_string());
-            Err(process_error(msg, output.status.clone(), Some(output)))
+            Err(process_error(msg(), &command, Some(&output.status), Some(&output)))
         }
     }
 
index 8d26fc1a5e3af0d8389e8a2cfe5bf1e3c458ff75..8e0b7b563ba821eaa529484dba71b6ccd11c6bab 100644 (file)
-use std::fmt;
-use std::fmt::{Show,Formatter};
-use std::io;
-use std::io::IoError;
-use std::io::process::{ProcessOutput,ProcessExit};
-use core::errors::{CLIError,CLIResult};
-use toml;
-
-/*
- * CargoResult should be used in libcargo. CargoCliResult should be used in the
- * various executables.
- */
-
-pub type CargoResult<T> = Result<T, CargoError>;
-
-pub fn other_error(desc: &'static str) -> CargoError {
-    CargoError {
-        kind: OtherCargoError,
-        desc: StaticDescription(desc),
-        detail: None,
-        cause: None
-    }
-}
-
-pub fn io_error(err: IoError) -> CargoError {
-    let desc = err.desc;
-
-    CargoError {
-        kind: IoError(err),
-        desc: StaticDescription(desc),
-        detail: None,
-        cause: None
-    }
-}
-
-pub fn process_error(detail: String, exit: ProcessExit, output: Option<ProcessOutput>) -> CargoError {
-    CargoError {
-        kind: ProcessError(exit, output),
-        desc: BoxedDescription(detail),
-        detail: None,
-        cause: None
-    }
-}
-
-pub fn human_error<T: ToStr, U: ToStr>(desc: T, detail: U, cause: CargoError) -> CargoError {
-    CargoError {
-        kind: HumanReadableError,
-        desc: BoxedDescription(desc.to_str()),
-        detail: Some(detail.to_str()),
-        cause: Some(box cause)
-    }
-}
-
-pub fn simple_human<T: Show>(desc: T) -> CargoError {
-    CargoError {
-        kind: HumanReadableError,
-        desc: BoxedDescription(desc.to_str()),
-        detail: None,
-        cause: None
-    }
-}
-
-pub fn toml_error(desc: &'static str, error: toml::Error) -> CargoError {
-    CargoError {
-        kind: TomlError(error),
-        desc: StaticDescription(desc),
-        detail: None,
-        cause: None
-    }
-}
-
-#[deriving(PartialEq,Clone)]
-pub struct CargoError {
-    pub kind: CargoErrorKind,
-    desc: CargoErrorDescription,
-    detail: Option<String>,
-    cause: Option<Box<CargoError>>
-}
-
-impl Show for CargoError {
-    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
-        match self.desc {
-            StaticDescription(string) => try!(write!(f, "{}", string)),
-            BoxedDescription(ref string) => try!(write!(f, "{}", string))
-        };
-
-        write!(f, "; kind={}", self.kind)
-    }
-}
-
-#[deriving(PartialEq,Show,Clone)]
-enum CargoErrorDescription {
-    StaticDescription(&'static str),
-    BoxedDescription(String)
-}
-
-impl CargoError {
-    pub fn get_desc<'a>(&'a self) -> &'a str {
-        match self.desc {
-            StaticDescription(desc) => desc,
-            BoxedDescription(ref desc) => desc.as_slice()
-        }
-    }
-
-    pub fn get_detail<'a>(&'a self) -> Option<&'a str> {
-        self.detail.as_ref().map(|s| s.as_slice())
-    }
-
-    pub fn with_detail<T: Show>(mut self, detail: T) -> CargoError {
-        self.detail = Some(detail.to_str());
-        self
-    }
-
-    pub fn to_cli(self, exit_code: uint) -> CLIError {
-        match self {
-            CargoError { kind: HumanReadableError, desc: BoxedDescription(desc), detail: detail, .. } => {
-                CLIError::new(desc, detail, exit_code)
-            },
-            ref err @ CargoError { kind: InternalError, desc: StaticDescription(desc), detail: None, .. } => {
-                CLIError::new(format!("An unexpected error occurred: {}", err), Some(desc), exit_code)
-            },
-            ref err @ CargoError { kind: InternalError, desc: StaticDescription(desc), detail: Some(ref detail), .. } => {
-                CLIError::new(format!("An unexpected error occurred: {}", err), Some(format!("{}\n{}", desc, detail)), exit_code)
-            },
-            ref err @ _ => {
-                CLIError::new(format!("An unexpected error occurred: {}", err), None::<&str>, exit_code)
-            }
-        }
-    }
-}
-
-#[deriving(PartialEq)]
-pub enum CargoErrorKind {
-    HumanReadableError,
-    InternalError,
-    ProcessError(ProcessExit, Option<ProcessOutput>),
-    IoError(io::IoError),
-    TomlError(toml::Error),
-    OtherCargoError
-}
-
-impl Show for CargoErrorKind {
-    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
-        match self {
-            &ProcessError(ref exit, _) => write!(f, "ProcessError({})", exit),
-            &HumanReadableError => write!(f, "HumanReadableError"),
-            &InternalError => write!(f, "InternalError"),
-            &IoError(ref err) => write!(f, "IoError({})", err),
-            &TomlError(ref err) => write!(f, "TomlError({})", err),
-            &OtherCargoError => write!(f, "OtherCargoError")
-        }
-    }
-}
-
-impl Clone for CargoErrorKind {
-    fn clone(&self) -> CargoErrorKind {
-        match self {
-            &ProcessError(ref exit, ref output) => {
-                ProcessError(exit.clone(), output.as_ref().map(|output| ProcessOutput {
-                    status: output.status.clone(), output: output.output.clone(), error: output.error.clone()
-                }))
-            },
-            &HumanReadableError => HumanReadableError,
-            &InternalError => InternalError,
-            &IoError(ref err) => IoError(err.clone()),
-            &TomlError(ref err) => TomlError(err.clone()),
-            &OtherCargoError => OtherCargoError
-        }
-    }
-}
-
-type CargoCliResult<T> = Result<T, CargoCliError>;
-
-#[deriving(Show,Clone)]
-pub struct CargoCliError {
-    kind: CargoCliErrorKind,
-    exit_status: uint,
-    desc: &'static str,
-    detail: Option<String>,
-    cause: Option<CargoError>
-}
-
-#[deriving(Show,Clone)]
-pub enum CargoCliErrorKind {
-    OtherCargoCliError
-}
+use util::errors::{CargoResult, CargoError, chain};
 
 pub trait Wrap {
-    fn wrap(self, desc: &'static str) -> Self;
+    fn wrap<E: CargoError>(self, error: E) -> Self;
 }
 
-impl<T> Wrap for Result<T, CargoError> {
-    fn wrap(self, desc: &'static str) -> Result<T, CargoError> {
+impl<T> Wrap for Result<T, Box<CargoError>> {
+    fn wrap<E: CargoError>(self, error: E) -> CargoResult<T> {
         match self {
             Ok(x) => Ok(x),
-            Err(e) => {
-                Err(CargoError {
-                    kind: e.kind.clone(),
-                    desc: StaticDescription(desc),
-                    detail: None,
-                    cause: Some(box e)
-                })
-            }
+            Err(e) => Err(chain(e, error))
         }
     }
 }
 
 pub trait Require<T> {
-    fn require(self, err: CargoError) -> CargoResult<T>;
+    fn require<E: CargoError>(self, err: || -> E) -> CargoResult<T>;
 }
 
 impl<T> Require<T> for Option<T> {
-    fn require(self, err: CargoError) -> CargoResult<T> {
+    fn require<E: CargoError>(self, err: || -> E) -> CargoResult<T> {
         match self {
             Some(x) => Ok(x),
-            None => Err(err)
-        }
-    }
-}
-
-pub trait ToCLI<T> {
-    fn to_cli(self, exit_code: uint) -> CLIResult<T>;
-}
-
-impl<T> ToCLI<T> for Result<T, CargoError> {
-    fn to_cli(self, exit_code: uint) -> CLIResult<T> {
-        match self {
-            Ok(val) => Ok(val),
-            Err(err) => Err(err.to_cli(exit_code))
+            None => Err(box err().concrete() as Box<CargoError>)
         }
     }
 }
index 0bf40537faa6e5cde8ca82fa35ec9bc270d1227b..261b9413b89ce2eeb612595605030c6d9a909793 100644 (file)
@@ -7,14 +7,11 @@ use serialize::Decodable;
 use core::{SourceId,GitKind};
 use core::manifest::{LibKind,Lib};
 use core::{Summary,Manifest,Target,Dependency,PackageId};
-use util::{CargoResult,Require,simple_human,toml_error};
+use util::{CargoResult, Require, error, box_error};
 
 pub fn to_manifest(contents: &[u8], source_id: &SourceId) -> CargoResult<(Manifest, Vec<Path>)> {
-    let root = try!(toml::parse_from_bytes(contents).map_err(|_|
-        simple_human("Cargo.toml is not valid Toml")));
-
-    let toml = try!(toml_to_manifest(root).map_err(|_|
-        simple_human("Cargo.toml is not a valid Cargo manifest")));
+    let root = try!(toml::parse_from_bytes(contents).map_err(|_| error("Cargo.toml is not valid Toml")));
+    let toml = try!(toml_to_manifest(root).map_err(|_| error("Cargo.toml is not a valid manifest")));
 
     toml.to_manifest(source_id)
 }
@@ -28,7 +25,7 @@ fn toml_to_manifest(root: toml::Value) -> CargoResult<TomlManifest> {
         toml::from_toml(root.clone())
     }
 
-    let project = try!(decode(&root, "project").map_err(|e| toml_error("ZOMG", e)));
+    let project = try!(decode(&root, "project").map_err(box_error));
     let lib = decode(&root, "lib").ok();
     let bin = decode(&root, "bin").ok();
 
@@ -36,7 +33,7 @@ fn toml_to_manifest(root: toml::Value) -> CargoResult<TomlManifest> {
 
     let deps = match deps {
         Some(deps) => {
-            let table = try!(deps.get_table().require(simple_human("dependencies must be a table"))).clone();
+            let table = try!(deps.get_table().require(|| "dependencies must be a table")).clone();
 
             let mut deps: HashMap<String, TomlDependency> = HashMap::new();
 
@@ -48,13 +45,13 @@ fn toml_to_manifest(root: toml::Value) -> CargoResult<TomlManifest> {
 
                         for (k, v) in table.iter() {
                             let v = try!(v.get_str()
-                                         .require(simple_human("dependency values must be string")));
+                                         .require(|| "dependency values must be string"));
 
                             details.insert(k.clone(), v.clone());
                         }
 
                         let version = try!(details.find_equiv(&"version")
-                                           .require(simple_human("dependencies must include a version"))).clone();
+                                           .require(|| "dependencies must include a version")).clone();
 
                         deps.insert(k.clone(), DetailedDep(DetailedTomlDependency {
                             version: version,
index a7cde6a6553d334a45d2122a307fb2d2e7344066..552852a3d8f38a0aefcce1f9cbaf7e74e170fade 100644 (file)
@@ -11,7 +11,7 @@ use std::fmt::Show;
 use ham = hamcrest;
 use cargo::core::shell;
 use cargo::util::{process,ProcessBuilder,CargoError};
-use cargo::util::result::ProcessError;
+use cargo::util::ProcessError;
 
 pub mod paths;
 
@@ -253,7 +253,7 @@ impl ham::Matcher<ProcessBuilder> for Execs {
 
     match res {
       Ok(out) => self.match_output(&out),
-      Err(CargoError { kind: ProcessError(_, ref out), .. }) => self.match_output(out.get_ref()),
+      Err(ProcessError { output: Some(ref out), .. }) => self.match_output(out),
       Err(e) => Err(format!("could not exec process {}: {}", process, e))
     }
   }
index 39cc570269fe1cc82e7d2d9387e38f8244191ac5..52635b4d3235567d2d677b7389b9be5ea202c064 100644 (file)
@@ -42,7 +42,7 @@ test!(cargo_compile_with_invalid_manifest {
     assert_that(p.cargo_process("cargo-compile"),
         execs()
         .with_status(101)
-        .with_stderr("Cargo.toml is not a valid Cargo manifest"));
+        .with_stderr("Cargo.toml is not a valid manifest"));
 })
 
 test!(cargo_compile_without_manifest {
@@ -64,7 +64,7 @@ test!(cargo_compile_with_invalid_code {
     assert_that(p.cargo_process("cargo-compile"),
         execs()
         .with_status(101)
-        .with_stderr(format!("src/foo.rs:1:1: 1:8 error: expected item but found `invalid`\nsrc/foo.rs:1 invalid rust code!\n             ^~~~~~~\nfailed to execute: `rustc src/foo.rs --crate-type bin --out-dir {} -L {}`", target.display(), target.join("deps").display()).as_slice()));
+        .with_stderr(format!("src/foo.rs:1:1: 1:8 error: expected item but found `invalid`\nsrc/foo.rs:1 invalid rust code!\n             ^~~~~~~\nExecuting `rustc 'src/foo.rs' '--crate-type' 'bin' '--out-dir' '{}' '-L' '{}'` failed (status=101)", target.display(), target.join("deps").display()).as_slice()));
 })
 
 test!(cargo_compile_with_warnings_in_the_root_package {
index 94bcbede13dcb5ceaede7ef24daef0c2a62f564d..117b7edc787419761a13d065aa48a17d30036a0e 100644 (file)
@@ -1,12 +1,25 @@
-use support::{ProjectBuilder,ResultTest,project,execs,main_file};
+use std::io::File;
+
+use support::{ProjectBuilder, ResultTest, project, execs, main_file, paths};
 use hamcrest::{assert_that,existing_file};
 use cargo;
-use cargo::util::{CargoResult,process};
+use cargo::util::{CargoResult, ProcessError, process};
 
 fn setup() {
 }
 
-fn git_repo(name: &str, callback: |ProjectBuilder| -> ProjectBuilder) -> CargoResult<ProjectBuilder> {
+fn git_repo(name: &str, callback: |ProjectBuilder| -> ProjectBuilder) -> Result<ProjectBuilder, ProcessError> {
+    let gitconfig = paths::home().join(".gitconfig");
+
+    if !gitconfig.exists() {
+        File::create(&gitconfig).write(r"
+            [user]
+
+            email = foo@bar.com
+            name = Foo Bar
+        ".as_bytes()).assert()
+    }
+
     let mut git_project = project(name);
     git_project = callback(git_project);
     git_project.build();