From f53f11a9f1bbff26ddeb026905484be5d5a77d66 Mon Sep 17 00:00:00 2001 From: timvisee Date: Tue, 26 Feb 2019 15:28:13 +0100 Subject: [PATCH] Add version subcommand to determine remote server API version --- src/action/mod.rs | 1 + src/action/version.rs | 62 ++++++++++++++++++++++++++++++++++++++ src/cmd/handler.rs | 11 +++++-- src/cmd/matcher/mod.rs | 4 ++- src/cmd/matcher/version.rs | 30 ++++++++++++++++++ src/cmd/subcmd/mod.rs | 4 ++- src/cmd/subcmd/version.rs | 16 ++++++++++ src/error.rs | 11 +++++++ src/main.rs | 8 +++++ 9 files changed, 143 insertions(+), 4 deletions(-) create mode 100644 src/action/version.rs create mode 100644 src/cmd/matcher/version.rs create mode 100644 src/cmd/subcmd/version.rs diff --git a/src/action/mod.rs b/src/action/mod.rs index aeea785..3589a66 100644 --- a/src/action/mod.rs +++ b/src/action/mod.rs @@ -8,3 +8,4 @@ pub mod info; pub mod params; pub mod password; pub mod upload; +pub mod version; diff --git a/src/action/version.rs b/src/action/version.rs new file mode 100644 index 0000000..df2420a --- /dev/null +++ b/src/action/version.rs @@ -0,0 +1,62 @@ +use clap::ArgMatches; +use ffsend_api::action::version::{Error as VersionError, Version as ApiVersion}; +use ffsend_api::file::remote_file::{FileParseError, RemoteFile}; + +use crate::client::create_client; +use crate::cmd::matcher::main::MainMatcher; +use crate::cmd::matcher::{version::VersionMatcher, Matcher}; +use crate::error::ActionError; +#[cfg(feature = "history")] +use crate::history_tool; + +/// A file version action. +pub struct Version<'a> { + cmd_matches: &'a ArgMatches<'a>, +} + +impl<'a> Version<'a> { + /// Construct a new version action. + pub fn new(cmd_matches: &'a ArgMatches<'a>) -> Self { + Self { cmd_matches } + } + + /// Invoke the version action. + // TODO: create a trait for this method + pub fn invoke(&self) -> Result<(), ActionError> { + // Create the command matchers + let matcher_version = VersionMatcher::with(self.cmd_matches).unwrap(); + let matcher_main = MainMatcher::with(self.cmd_matches).unwrap(); + + // Get the host + let host = matcher_version.host(); + + // Create a reqwest client + let client = create_client(&matcher_main); + + // Make sure the file version + let response = ApiVersion::new(host).invoke(&client); + + // Print the result + match response { + Ok(v) => println!("API version: {}", v), + Err(VersionError::Unknown) => println!("Version: unknown"), + Err(VersionError::Unsupported(v)) => println!("Version: {} (unsupported)", v), + Err(e) => return Err(e.into()), + } + + Ok(()) + } +} + +#[derive(Debug, Fail)] +pub enum Error { + /// An error occurred while attempting to determine the Send server version. + #[fail(display = "failed to check the server version")] + Version(#[cause] VersionError), +} + +impl From for Error { + fn from(err: VersionError) -> Error { + Error::Version(err) + } +} diff --git a/src/cmd/handler.rs b/src/cmd/handler.rs index 0a00d18..dfd3de5 100644 --- a/src/cmd/handler.rs +++ b/src/cmd/handler.rs @@ -6,12 +6,13 @@ use clap::{App, AppSettings, Arg, ArgMatches}; use super::matcher::HistoryMatcher; use super::matcher::{ DebugMatcher, DeleteMatcher, DownloadMatcher, ExistsMatcher, InfoMatcher, Matcher, - ParamsMatcher, PasswordMatcher, UploadMatcher, + ParamsMatcher, PasswordMatcher, UploadMatcher, VersionMatcher, }; #[cfg(feature = "history")] use super::subcmd::CmdHistory; use super::subcmd::{ CmdDebug, CmdDelete, CmdDownload, CmdExists, CmdInfo, CmdParams, CmdPassword, CmdUpload, + CmdVersion, }; use crate::config::{CLIENT_TIMEOUT, CLIENT_TRANSFER_TIMEOUT}; #[cfg(feature = "history")] @@ -145,7 +146,8 @@ impl<'a: 'b, 'b> Handler<'a> { .subcommand(CmdInfo::build()) .subcommand(CmdParams::build()) .subcommand(CmdPassword::build()) - .subcommand(CmdUpload::build().display_order(1)); + .subcommand(CmdUpload::build().display_order(1)) + .subcommand(CmdVersion::build()); // With history support, a flag for the history file and incognito mode #[cfg(feature = "history")] @@ -239,4 +241,9 @@ impl<'a: 'b, 'b> Handler<'a> { pub fn upload(&'a self) -> Option { UploadMatcher::with(&self.matches) } + + /// Get the version sub command, if matched. + pub fn version(&'a self) -> Option { + VersionMatcher::with(&self.matches) + } } diff --git a/src/cmd/matcher/mod.rs b/src/cmd/matcher/mod.rs index 035d5ac..4ff52dc 100644 --- a/src/cmd/matcher/mod.rs +++ b/src/cmd/matcher/mod.rs @@ -9,8 +9,9 @@ pub mod main; pub mod params; pub mod password; pub mod upload; +pub mod version; -// Reexport to matcher module +// Re-export to matcher module pub use self::debug::DebugMatcher; pub use self::delete::DeleteMatcher; pub use self::download::DownloadMatcher; @@ -22,6 +23,7 @@ pub use self::main::MainMatcher; pub use self::params::ParamsMatcher; pub use self::password::PasswordMatcher; pub use self::upload::UploadMatcher; +pub use self::version::VersionMatcher; use clap::ArgMatches; diff --git a/src/cmd/matcher/version.rs b/src/cmd/matcher/version.rs new file mode 100644 index 0000000..2596459 --- /dev/null +++ b/src/cmd/matcher/version.rs @@ -0,0 +1,30 @@ +use ffsend_api::url::Url; + +use clap::ArgMatches; + +use super::Matcher; +use crate::cmd::arg::{ArgHost, CmdArgOption}; + +/// The version command matcher. +pub struct VersionMatcher<'a> { + matches: &'a ArgMatches<'a>, +} + +impl<'a: 'b, 'b> VersionMatcher<'a> { + /// Get the host to probe. + /// + /// This method parses the host into an `Url`. + /// If the given host is invalid, + /// the program will quit with an error message. + pub fn host(&'a self) -> Url { + ArgHost::value(self.matches) + } +} + +impl<'a> Matcher<'a> for VersionMatcher<'a> { + fn with(matches: &'a ArgMatches) -> Option { + matches + .subcommand_matches("version") + .map(|matches| VersionMatcher { matches }) + } +} diff --git a/src/cmd/subcmd/mod.rs b/src/cmd/subcmd/mod.rs index cdf80db..7715e8e 100644 --- a/src/cmd/subcmd/mod.rs +++ b/src/cmd/subcmd/mod.rs @@ -8,8 +8,9 @@ pub mod info; pub mod params; pub mod password; pub mod upload; +pub mod version; -// Reexport to cmd module +// Re-export to cmd module pub use self::debug::CmdDebug; pub use self::delete::CmdDelete; pub use self::download::CmdDownload; @@ -20,3 +21,4 @@ pub use self::info::CmdInfo; pub use self::params::CmdParams; pub use self::password::CmdPassword; pub use self::upload::CmdUpload; +pub use self::version::CmdVersion; diff --git a/src/cmd/subcmd/version.rs b/src/cmd/subcmd/version.rs new file mode 100644 index 0000000..5edced0 --- /dev/null +++ b/src/cmd/subcmd/version.rs @@ -0,0 +1,16 @@ +use clap::{App, SubCommand}; + +use crate::cmd::arg::{ArgHost, CmdArg}; + +/// The version command definition. +pub struct CmdVersion; + +impl CmdVersion { + pub fn build<'a, 'b>() -> App<'a, 'b> { + SubCommand::with_name("version") + .about("Determine the Send server version") + .alias("ver") + .visible_alias("v") + .arg(ArgHost::build()) + } +} diff --git a/src/error.rs b/src/error.rs index 18bf5cf..ec35687 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,6 +2,7 @@ use ffsend_api::action::delete::Error as DeleteError; use ffsend_api::action::exists::Error as ExistsError; use ffsend_api::action::params::Error as ParamsError; use ffsend_api::action::password::Error as PasswordError; +use ffsend_api::action::version::Error as VersionError; use ffsend_api::file::remote_file::FileParseError; use crate::action::download::Error as CliDownloadError; @@ -72,6 +73,10 @@ pub enum ActionError { #[fail(display = "failed to change the password")] Password(#[cause] PasswordError), + /// An error occurred while invoking the version action. + #[fail(display = "failed to determine server version")] + Version(#[cause] VersionError), + /// An error occurred while invoking the upload action. #[fail(display = "failed to upload the specified file")] Upload(#[cause] CliUploadError), @@ -113,6 +118,12 @@ impl From for ActionError { } } +impl From for ActionError { + fn from(err: VersionError) -> ActionError { + ActionError::Version(err) + } +} + impl From for ActionError { fn from(err: FileParseError) -> ActionError { ActionError::InvalidUrl(err) diff --git a/src/main.rs b/src/main.rs index 0f4f9fb..3e05666 100644 --- a/src/main.rs +++ b/src/main.rs @@ -43,6 +43,7 @@ use crate::action::info::Info; use crate::action::params::Params; use crate::action::password::Password; use crate::action::upload::Upload; +use crate::action::version::Version; use crate::cmd::{ matcher::{MainMatcher, Matcher}, Handler, @@ -135,6 +136,13 @@ fn invoke_action(handler: &Handler) -> Result<(), Error> { .map_err(|err| err.into()); } + // Match the version command + if handler.version().is_some() { + return Version::new(handler.matches()) + .invoke() + .map_err(|err| err.into()); + } + // Get the main matcher let matcher_main = MainMatcher::with(handler.matches()).unwrap();