Add CLI error structs, improve routing and error reporting from API
This commit is contained in:
parent
affa6e65d2
commit
b2e63b9efc
8 changed files with 61 additions and 19 deletions
|
@ -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
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -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)",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
cli/src/error.rs
Normal file
20
cli/src/error.rs
Normal file
|
@ -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),
|
||||
}
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue