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:
|
script:
|
||||||
- cargo build --verbose --all
|
- cargo build --verbose --all
|
||||||
- cargo build --no-default-features --verbose --all
|
- cargo build --no-default-features --verbose --all
|
||||||
- cargo build --features no-color --verbose --all
|
|
||||||
- cargo test --verbose --all
|
- cargo test --verbose --all
|
||||||
- cargo doc
|
- 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)",
|
"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)",
|
"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 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",
|
"ffsend-api 0.1.0",
|
||||||
"open 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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 }
|
clipboard = { version = "0.4", optional = true }
|
||||||
colored = "1.6"
|
colored = "1.6"
|
||||||
failure = "0.1"
|
failure = "0.1"
|
||||||
|
failure_derive = "0.1"
|
||||||
ffsend-api = { version = "*", path = "../api" }
|
ffsend-api = { version = "*", path = "../api" }
|
||||||
open = "1"
|
open = "1"
|
||||||
pbr = "1"
|
pbr = "1"
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use failure::Fail;
|
|
||||||
use ffsend_api::action::download::Download as ApiDownload;
|
use ffsend_api::action::download::Download as ApiDownload;
|
||||||
use ffsend_api::file::file::DownloadFile;
|
use ffsend_api::file::file::DownloadFile;
|
||||||
use ffsend_api::reqwest::Client;
|
use ffsend_api::reqwest::Client;
|
||||||
|
|
||||||
use cmd::cmd_download::CmdDownload;
|
use cmd::cmd_download::CmdDownload;
|
||||||
|
use error::ActionError;
|
||||||
use progress::ProgressBar;
|
use progress::ProgressBar;
|
||||||
use util::quit_error;
|
|
||||||
|
|
||||||
/// A file download action.
|
/// A file download action.
|
||||||
pub struct Download<'a> {
|
pub struct Download<'a> {
|
||||||
|
@ -24,7 +23,7 @@ impl<'a> Download<'a> {
|
||||||
|
|
||||||
/// Invoke the download action.
|
/// Invoke the download action.
|
||||||
// TODO: create a trait for this method
|
// TODO: create a trait for this method
|
||||||
pub fn invoke(&self) {
|
pub fn invoke(&self) -> Result<(), ActionError> {
|
||||||
// Get the download URL
|
// Get the download URL
|
||||||
let url = self.cmd.url();
|
let url = self.cmd.url();
|
||||||
|
|
||||||
|
@ -40,11 +39,12 @@ impl<'a> Download<'a> {
|
||||||
|
|
||||||
// Execute an download action
|
// Execute an download action
|
||||||
// TODO: do not unwrap, but return an error
|
// TODO: do not unwrap, but return an error
|
||||||
if let Err(err) = ApiDownload::new(&file).invoke(&client, bar) {
|
ApiDownload::new(&file).invoke(&client, bar)
|
||||||
quit_error(err.context("Failed to download the requested file"));
|
.map_err(|err| ActionError::Download(err))?;
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: open the file, or it's location
|
// TODO: open the file, or it's location
|
||||||
// TODO: copy the file 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 ffsend_api::reqwest::Client;
|
||||||
|
|
||||||
use cmd::cmd_upload::CmdUpload;
|
use cmd::cmd_upload::CmdUpload;
|
||||||
|
use error::ActionError;
|
||||||
use progress::ProgressBar;
|
use progress::ProgressBar;
|
||||||
use util::open_url;
|
use util::open_url;
|
||||||
#[cfg(feature = "clipboard")]
|
#[cfg(feature = "clipboard")]
|
||||||
|
@ -25,7 +26,7 @@ impl<'a> Upload<'a> {
|
||||||
|
|
||||||
/// Invoke the upload action.
|
/// Invoke the upload action.
|
||||||
// TODO: create a trait for this method
|
// TODO: create a trait for this method
|
||||||
pub fn invoke(&self) {
|
pub fn invoke(&self) -> Result<(), ActionError> {
|
||||||
// Get API parameters
|
// Get API parameters
|
||||||
let path = Path::new(self.cmd.file()).to_path_buf();
|
let path = Path::new(self.cmd.file()).to_path_buf();
|
||||||
let host = self.cmd.host();
|
let host = self.cmd.host();
|
||||||
|
@ -59,5 +60,7 @@ impl<'a> Upload<'a> {
|
||||||
.expect("failed to put download URL in user clipboard");
|
.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;
|
extern crate failure;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate failure_derive;
|
||||||
extern crate ffsend_api;
|
extern crate ffsend_api;
|
||||||
|
|
||||||
mod action;
|
mod action;
|
||||||
mod app;
|
mod app;
|
||||||
mod cmd;
|
mod cmd;
|
||||||
|
mod error;
|
||||||
mod progress;
|
mod progress;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
use action::download::Download;
|
use action::download::Download;
|
||||||
use action::upload::Upload;
|
use action::upload::Upload;
|
||||||
use cmd::Handler;
|
use cmd::Handler;
|
||||||
|
use error::Error;
|
||||||
|
use util::quit_error;
|
||||||
|
|
||||||
/// Application entrypoint.
|
/// Application entrypoint.
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -17,26 +22,32 @@ fn main() {
|
||||||
let cmd_handler = Handler::parse();
|
let cmd_handler = Handler::parse();
|
||||||
|
|
||||||
// Invoke the proper action
|
// 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.
|
/// Invoke the proper action based on the CLI input.
|
||||||
///
|
///
|
||||||
/// If no proper action is selected, the program will quit with an error
|
/// If no proper action is selected, the program will quit with an error
|
||||||
/// message.
|
/// message.
|
||||||
fn invoke_action(handler: &Handler) {
|
fn invoke_action(handler: &Handler) -> Result<(), Error> {
|
||||||
// Match the upload command
|
// Match the upload command
|
||||||
if let Some(cmd) = handler.upload() {
|
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
|
// Match the download command
|
||||||
if let Some(cmd) = handler.download() {
|
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
|
// No subcommand was selected, show general help
|
||||||
Handler::build()
|
Handler::build()
|
||||||
.print_help()
|
.print_help()
|
||||||
.expect("failed to print command 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,
|
/// Print the given error in a proper format for the user,
|
||||||
/// with it's causes.
|
/// with it's causes.
|
||||||
pub fn print_error<E: Fail>(err: E) {
|
pub fn print_error<E: Fail>(err: E) {
|
||||||
// Print the main error
|
// Report each printable error, count them
|
||||||
eprintln!("{} {}", "error:".red().bold(), err);
|
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
|
// Fall back to a basic message
|
||||||
let mut cause = err.cause();
|
if count == 0 {
|
||||||
while let Some(err) = cause {
|
eprintln!("{} {}", "error:".red().bold(), "An undefined error occurred");
|
||||||
eprintln!("{} {}", "caused by:".red().bold(), err);
|
|
||||||
cause = err.cause();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue