Kaynağa Gözat

Create CLI exists command

timvisee 7 yıl önce
ebeveyn
işleme
2719bb4834

+ 83 - 0
cli/src/action/exists.rs

@@ -0,0 +1,83 @@
+use clap::ArgMatches;
+use ffsend_api::action::exists::{
+    Error as ExistsError,
+    Exists as ApiExists,
+};
+use ffsend_api::file::remote_file::{
+    FileParseError,
+    RemoteFile,
+};
+use ffsend_api::reqwest::Client;
+
+use cmd::matcher::{
+    Matcher,
+    exists::ExistsMatcher,
+};
+use error::ActionError;
+
+/// A file exists action.
+pub struct Exists<'a> {
+    cmd_matches: &'a ArgMatches<'a>,
+}
+
+impl<'a> Exists<'a> {
+    /// Construct a new exists action.
+    pub fn new(cmd_matches: &'a ArgMatches<'a>) -> Self {
+        Self {
+            cmd_matches,
+        }
+    }
+
+    /// Invoke the exists action.
+    // TODO: create a trait for this method
+    pub fn invoke(&self) -> Result<(), ActionError> {
+        // Create the command matchers
+        let matcher_exists = ExistsMatcher::with(self.cmd_matches).unwrap();
+
+        // Get the share URL
+        let url = matcher_exists.url();
+
+        // Create a reqwest client
+        let client = Client::new();
+
+        // Parse the remote file based on the share URL
+        let file = RemoteFile::parse_url(url, None)?;
+
+        // Make sure the file exists
+        let exists_response = ApiExists::new(&file)
+            .invoke(&client)?;
+        let exists = exists_response.exists();
+
+        // Print the results
+        println!("Exists: {:?}", exists);
+        if exists {
+            println!("Password: {:?}", exists_response.has_password());
+        }
+
+        Ok(())
+    }
+}
+
+#[derive(Debug, Fail)]
+pub enum Error {
+    /// Failed to parse a share URL, it was invalid.
+    /// This error is not related to a specific action.
+    #[fail(display = "Invalid share URL")]
+    InvalidUrl(#[cause] FileParseError),
+
+    /// An error occurred while checking if the file exists.
+    #[fail(display = "Failed to check whether the file exists")]
+    Exists(#[cause] ExistsError),
+}
+
+impl From<FileParseError> for Error {
+    fn from(err: FileParseError) -> Error {
+        Error::InvalidUrl(err)
+    }
+}
+
+impl From<ExistsError> for Error {
+    fn from(err: ExistsError) -> Error {
+        Error::Exists(err)
+    }
+}

+ 1 - 0
cli/src/action/mod.rs

@@ -1,5 +1,6 @@
 pub mod delete;
 pub mod download;
+pub mod exists;
 pub mod info;
 pub mod params;
 pub mod password;

+ 16 - 0
cli/src/cmd/cmd/exists.rs

@@ -0,0 +1,16 @@
+use clap::{App, SubCommand};
+
+use cmd::arg::{ArgUrl, CmdArg};
+
+/// The exists command definition.
+pub struct CmdExists;
+
+impl CmdExists {
+    pub fn build<'a, 'b>() -> App<'a, 'b> {
+        SubCommand::with_name("exists")
+            .about("Check whether a remote file exists.")
+            .visible_alias("e")
+            .alias("exist")
+            .arg(ArgUrl::build())
+    }
+}

+ 2 - 0
cli/src/cmd/cmd/mod.rs

@@ -1,5 +1,6 @@
 pub mod delete;
 pub mod download;
+pub mod exists;
 pub mod info;
 pub mod params;
 pub mod password;
@@ -8,6 +9,7 @@ pub mod upload;
 // Reexport to cmd module
 pub use self::delete::CmdDelete;
 pub use self::download::CmdDownload;
+pub use self::exists::CmdExists;
 pub use self::info::CmdInfo;
 pub use self::params::CmdParams;
 pub use self::password::CmdPassword;

+ 8 - 0
cli/src/cmd/handler.rs

@@ -5,6 +5,7 @@ use app::*;
 use super::matcher::{
     DeleteMatcher,
     DownloadMatcher,
+    ExistsMatcher,
     InfoMatcher,
     Matcher,
     ParamsMatcher,
@@ -14,6 +15,7 @@ use super::matcher::{
 use super::cmd::{
     CmdDelete,
     CmdDownload,
+    CmdExists,
     CmdInfo,
     CmdParams,
     CmdPassword,
@@ -55,6 +57,7 @@ impl<'a: 'b, 'b> Handler<'a> {
                 .help("Assume yes for prompts"))
             .subcommand(CmdDelete::build())
             .subcommand(CmdDownload::build().display_order(2))
+            .subcommand(CmdExists::build())
             .subcommand(CmdInfo::build())
             .subcommand(CmdParams::build())
             .subcommand(CmdPassword::build())
@@ -84,6 +87,11 @@ impl<'a: 'b, 'b> Handler<'a> {
         DownloadMatcher::with(&self.matches)
     }
 
+    /// Get the exists sub command, if matched.
+    pub fn exists(&'a self) -> Option<ExistsMatcher> {
+        ExistsMatcher::with(&self.matches)
+    }
+
     /// Get the info matcher, if that subcommand is entered.
     pub fn info(&'a self) -> Option<InfoMatcher> {
         InfoMatcher::with(&self.matches)

+ 33 - 0
cli/src/cmd/matcher/exists.rs

@@ -0,0 +1,33 @@
+use ffsend_api::url::Url;
+
+use clap::ArgMatches;
+
+use cmd::arg::{ArgUrl, CmdArgOption};
+use super::Matcher;
+
+/// The exists command matcher.
+pub struct ExistsMatcher<'a> {
+    matches: &'a ArgMatches<'a>,
+}
+
+impl<'a: 'b, 'b> ExistsMatcher<'a> {
+    /// Get the file share URL.
+    ///
+    /// This method parses the URL into an `Url`.
+    /// If the given URL is invalid,
+    /// the program will quit with an error message.
+    pub fn url(&'a self) -> Url {
+        ArgUrl::value(self.matches)
+    }
+}
+
+impl<'a> Matcher<'a> for ExistsMatcher<'a> {
+    fn with(matches: &'a ArgMatches) -> Option<Self> {
+        matches.subcommand_matches("exists")
+            .map(|matches|
+                 ExistsMatcher {
+                     matches,
+                 }
+            )
+    }
+}

+ 2 - 0
cli/src/cmd/matcher/mod.rs

@@ -1,5 +1,6 @@
 pub mod delete;
 pub mod download;
+pub mod exists;
 pub mod info;
 pub mod params;
 pub mod password;
@@ -8,6 +9,7 @@ pub mod upload;
 // Reexport to matcher module
 pub use self::delete::DeleteMatcher;
 pub use self::download::DownloadMatcher;
+pub use self::exists::ExistsMatcher;
 pub use self::info::InfoMatcher;
 pub use self::params::ParamsMatcher;
 pub use self::password::PasswordMatcher;

+ 11 - 0
cli/src/error.rs

@@ -1,5 +1,6 @@
 use ffsend_api::action::delete::Error as DeleteError;
 use ffsend_api::action::download::Error as DownloadError;
+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::upload::Error as UploadError;
@@ -36,6 +37,10 @@ pub enum ActionError {
     #[fail(display = "Failed to download the requested file")]
     Download(#[cause] DownloadError),
 
+    /// An error occurred while invoking the exists action.
+    #[fail(display = "Failed to check whether the file exists")]
+    Exists(#[cause] ExistsError),
+
     /// An error occurred while invoking the info action.
     #[fail(display = "Failed to fetch file info")]
     Info(#[cause] InfoError),
@@ -71,6 +76,12 @@ impl From<DownloadError> for ActionError {
     }
 }
 
+impl From<ExistsError> for ActionError {
+    fn from(err: ExistsError) -> ActionError {
+        ActionError::Exists(err)
+    }
+}
+
 impl From<InfoError> for ActionError {
     fn from(err: InfoError) -> ActionError {
         ActionError::Info(err)

+ 7 - 0
cli/src/main.rs

@@ -14,6 +14,7 @@ mod util;
 
 use action::delete::Delete;
 use action::download::Download;
+use action::exists::Exists;
 use action::info::Info;
 use action::params::Params;
 use action::password::Password;
@@ -50,6 +51,12 @@ fn invoke_action(handler: &Handler) -> Result<(), Error> {
             .map_err(|err| err.into());
     }
 
+    // Match the exists command
+    if handler.exists().is_some() {
+        return Exists::new(handler.matches()).invoke()
+            .map_err(|err| err.into());
+    }
+
     // Match the info command
     if handler.info().is_some() {
         return Info::new(handler.matches()).invoke()