Add --verbose cause chain support
authorYehuda Katz <wycats@gmail.com>
Thu, 19 Jun 2014 21:13:39 +0000 (14:13 -0700)
committerYehuda Katz <wycats@gmail.com>
Thu, 19 Jun 2014 21:15:17 +0000 (14:15 -0700)
libs/hammer.rs
src/bin/cargo.rs
src/cargo/lib.rs

index 673bbd95858fc0e6eae39d0d30a2781ba4c8d87e..9af78f5b1fdfd09580758ec3b559c7704e6a5382 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 673bbd95858fc0e6eae39d0d30a2781ba4c8d87e
+Subproject commit 9af78f5b1fdfd09580758ec3b559c7704e6a5382
index 75608afbe766bd74d8c932a5e626a9d1edfa9f51..d700a876f0a331ae1d95aa0d776a818cedcb48f4 100644 (file)
@@ -34,7 +34,7 @@ fn execute() {
 
     let (cmd, args) = match process(os::args()) {
         Ok((cmd, args)) => (cmd, args),
-        Err(err) => return handle_error(err)
+        Err(err) => return handle_error(err, false)
     };
 
     if cmd == "config-for-key".to_str() {
@@ -59,8 +59,8 @@ fn execute() {
 
         match command {
             Ok(ExitStatus(0)) => (),
-            Ok(ExitStatus(i)) | Ok(ExitSignal(i)) => handle_error(CliError::new("", i as uint)),
-            Err(_) => handle_error(CliError::new("No such subcommand", 127))
+            Ok(ExitStatus(i)) | Ok(ExitSignal(i)) => handle_error(CliError::new("", i as uint), false),
+            Err(_) => handle_error(CliError::new("No such subcommand", 127), false)
         }
     }
 }
index 61ce7ad09d7f8693ae8a5aeba39f31663e9574c2..106cb992c9b51f8bbc6aa3e99140cbd32584cfc6 100644 (file)
@@ -19,8 +19,8 @@ extern crate hamcrest;
 
 use serialize::{Decoder,Encoder,Decodable,Encodable,json};
 use std::io;
-use hammer::{FlagDecoder,FlagConfig,HammerError};
-pub use util::{CliError, CliResult, human};
+use hammer::{FlagDecoder, FlagConfig, HammerError, FlagConfiguration};
+pub use util::{CargoError, CliError, CliResult, human};
 
 macro_rules! some(
   ($e:expr) => (
@@ -53,30 +53,48 @@ pub struct NoFlags;
 
 impl FlagConfig for NoFlags {}
 
+#[deriving(Decodable)]
+pub struct GlobalFlags {
+    verbose: bool,
+    rest: Vec<String>
+}
+
+impl FlagConfig for GlobalFlags {
+    fn config(_: Option<GlobalFlags>, c: FlagConfiguration) -> FlagConfiguration {
+        c.short("verbose", '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>());
+    fn call<'a, T: RepresentsFlags, U: RepresentsJSON, V: Encodable<json::Encoder<'a>, io::IoError>>(exec: fn(T, U) -> CliResult<Option<V>>, args: &[String]) -> CliResult<Option<V>> {
+        let flags = try!(flags_from_args::<T>(args));
         let json = try!(json_from_stdin::<U>());
 
         exec(flags, json)
     }
 
-    process_executed(call(exec))
+    match global_flags() {
+        Err(e) => handle_error(e, true),
+        Ok(val) => process_executed(call(exec, val.rest.as_slice()), val)
+    }
 }
 
 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>());
+    fn call<'a, T: RepresentsFlags, V: Encodable<json::Encoder<'a>, io::IoError>>(exec: fn(T) -> CliResult<Option<V>>, args: &[String]) -> CliResult<Option<V>> {
+        let flags = try!(flags_from_args::<T>(args));
 
         exec(flags)
     }
 
-    process_executed(call(exec));
+    match global_flags() {
+        Err(e) => handle_error(e, true),
+        Ok(val) => process_executed(call(exec, val.rest.as_slice()), val)
+    }
 }
 
-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>>, flags: GlobalFlags) {
     match result {
-        Err(e) => handle_error(e),
+        Err(e) => handle_error(e, flags.verbose),
         Ok(encodable) => {
             encodable.map(|encodable| {
                 let encoded = json::Encoder::str_encode(&encodable);
@@ -86,23 +104,37 @@ 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, verbose: bool) {
     log!(4, "handle_error; err={}", err);
 
     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));
+
+    if verbose {
+        error.cause().map(handle_cause);
+    }
 
     std::os::set_exit_status(exit_code as int);
 }
 
+fn handle_cause(err: &CargoError) {
+    println!("\nCaused by:");
+    println!("  {}", err.description());
+
+    err.cause().map(handle_cause);
+}
+
 fn args() -> Vec<String> {
     std::os::args()
 }
 
-fn flags_from_args<T: RepresentsFlags>() -> CliResult<T> {
-    let mut decoder = FlagDecoder::new::<T>(args().tail());
+fn flags_from_args<T: RepresentsFlags>(args: &[String]) -> CliResult<T> {
+    let mut decoder = FlagDecoder::new::<T>(args);
+    Decodable::decode(&mut decoder).map_err(|e: HammerError| CliError::new(e.message, 1))
+}
+
+fn global_flags() -> CliResult<GlobalFlags> {
+    let mut decoder = FlagDecoder::new::<GlobalFlags>(args().tail());
     Decodable::decode(&mut decoder).map_err(|e: HammerError| CliError::new(e.message, 1))
 }