Browse Source

Add CLI error structs, improve routing and error reporting from API

timvisee 7 năm trước cách đây
mục cha
commit
b2e63b9efc
8 tập tin đã thay đổi với 61 bổ sung19 xóa
  1. 0 1
      .travis.yml
  2. 1 0
      Cargo.lock
  3. 1 0
      cli/Cargo.toml
  4. 6 6
      cli/src/action/download.rs
  5. 4 1
      cli/src/action/upload.rs
  6. 20 0
      cli/src/error.rs
  7. 15 4
      cli/src/main.rs
  8. 14 7
      cli/src/util.rs

+ 0 - 1
.travis.yml

@@ -15,6 +15,5 @@ addons:
 script:
 - cargo build --verbose --all
 - cargo build --no-default-features --verbose --all
-- cargo build --features no-color --verbose --all
 - cargo test --verbose --all
 - cargo doc

+ 1 - 0
Cargo.lock

@@ -341,6 +341,7 @@ dependencies = [
  "clipboard 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "colored 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "ffsend-api 0.1.0",
  "open 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "pbr 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",

+ 1 - 0
cli/Cargo.toml

@@ -17,6 +17,7 @@ clap = "2.31"
 clipboard = { version = "0.4", optional = true }
 colored = "1.6"
 failure = "0.1"
+failure_derive = "0.1"
 ffsend-api = { version = "*", path = "../api" }
 open = "1"
 pbr = "1"

+ 6 - 6
cli/src/action/download.rs

@@ -1,13 +1,12 @@
 use std::sync::{Arc, Mutex};
 
-use failure::Fail;
 use ffsend_api::action::download::Download as ApiDownload;
 use ffsend_api::file::file::DownloadFile;
 use ffsend_api::reqwest::Client;
 
 use cmd::cmd_download::CmdDownload;
+use error::ActionError;
 use progress::ProgressBar;
-use util::quit_error;
 
 /// A file download action.
 pub struct Download<'a> {
@@ -24,7 +23,7 @@ impl<'a> Download<'a> {
 
     /// Invoke the download action.
     // TODO: create a trait for this method
-    pub fn invoke(&self) {
+    pub fn invoke(&self) -> Result<(), ActionError> {
         // Get the download URL
         let url = self.cmd.url();
 
@@ -40,11 +39,12 @@ impl<'a> Download<'a> {
 
         // Execute an download action
         // TODO: do not unwrap, but return an error
-        if let Err(err) = ApiDownload::new(&file).invoke(&client, bar) {
-            quit_error(err.context("Failed to download the requested file"));
-        }
+        ApiDownload::new(&file).invoke(&client, bar)
+            .map_err(|err| ActionError::Download(err))?;
 
         // TODO: open the file, or it's location
         // TODO: copy the file location
+
+        Ok(())
     }
 }

+ 4 - 1
cli/src/action/upload.rs

@@ -5,6 +5,7 @@ use ffsend_api::action::upload::Upload as ApiUpload;
 use ffsend_api::reqwest::Client;
 
 use cmd::cmd_upload::CmdUpload;
+use error::ActionError;
 use progress::ProgressBar;
 use util::open_url;
 #[cfg(feature = "clipboard")]
@@ -25,7 +26,7 @@ impl<'a> Upload<'a> {
 
     /// Invoke the upload action.
     // TODO: create a trait for this method
-    pub fn invoke(&self) {
+    pub fn invoke(&self) -> Result<(), ActionError> {
         // Get API parameters
         let path = Path::new(self.cmd.file()).to_path_buf();
         let host = self.cmd.host();
@@ -59,5 +60,7 @@ impl<'a> Upload<'a> {
                     .expect("failed to put download URL in user clipboard");
             }
         }
+
+        Ok(())
     }
 }

+ 20 - 0
cli/src/error.rs

@@ -0,0 +1,20 @@
+use ffsend_api::action::download::Error as DownloadError;
+
+#[derive(Fail, Debug)]
+pub enum Error {
+    /// An error occurred while invoking an action.
+    #[fail(display = "")]
+    Action(#[cause] ActionError),
+}
+
+#[derive(Debug, Fail)]
+pub enum ActionError {
+    /// An error occurred while invoking the upload action.
+    // TODO: bind the upload cause here
+    #[fail(display = "Failed to upload the specified file")]
+    Upload,
+
+    /// An error occurred while invoking the download action.
+    #[fail(display = "Failed to download the requested file")]
+    Download(#[cause] DownloadError),
+}

+ 15 - 4
cli/src/main.rs

@@ -1,15 +1,20 @@
 extern crate failure;
+#[macro_use]
+extern crate failure_derive;
 extern crate ffsend_api;
 
 mod action;
 mod app;
 mod cmd;
+mod error;
 mod progress;
 mod util;
 
 use action::download::Download;
 use action::upload::Upload;
 use cmd::Handler;
+use error::Error;
+use util::quit_error;
 
 /// Application entrypoint.
 fn main() {
@@ -17,26 +22,32 @@ fn main() {
     let cmd_handler = Handler::parse();
 
     // Invoke the proper action
-    invoke_action(&cmd_handler);
+    if let Err(err) = invoke_action(&cmd_handler) {
+        quit_error(err);
+    };
 }
 
 /// Invoke the proper action based on the CLI input.
 ///
 /// If no proper action is selected, the program will quit with an error
 /// message.
-fn invoke_action(handler: &Handler) {
+fn invoke_action(handler: &Handler) -> Result<(), Error> {
     // Match the upload command
     if let Some(cmd) = handler.upload() {
-        return Upload::new(&cmd).invoke();
+        return Upload::new(&cmd).invoke()
+            .map_err(|err| Error::Action(err));
     }
 
     // Match the download command
     if let Some(cmd) = handler.download() {
-        return Download::new(&cmd).invoke();
+        return Download::new(&cmd).invoke()
+            .map_err(|err| Error::Action(err));
     }
 
     // No subcommand was selected, show general help
     Handler::build()
         .print_help()
         .expect("failed to print command help");
+
+    Ok(())
 }

+ 14 - 7
cli/src/util.rs

@@ -18,14 +18,21 @@ use ffsend_api::url::Url;
 /// Print the given error in a proper format for the user,
 /// with it's causes.
 pub fn print_error<E: Fail>(err: E) {
-    // Print the main error
-    eprintln!("{} {}", "error:".red().bold(), err);
+    // Report each printable error, count them
+    let count = err.causes()
+        .map(|err| format!("{}", err))
+        .filter(|err| !err.is_empty())
+        .enumerate()
+        .map(|(i, err)| if i == 0 {
+            eprintln!("{} {}", "error:".red().bold(), err);
+        } else {
+            eprintln!("{} {}", "caused by:".red().bold(), err);
+        })
+        .count();
 
-    // Print the causes
-    let mut cause = err.cause();
-    while let Some(err) = cause {
-        eprintln!("{} {}", "caused by:".red().bold(), err);
-        cause = err.cause();
+    // Fall back to a basic message
+    if count == 0 {
+        eprintln!("{} {}", "error:".red().bold(), "An undefined error occurred");
     }
 }