mirror of
https://github.com/timvisee/ffsend.git
synced 2025-10-03 17:49:15 +02:00
Redo CLI command handling, make it modular with detached matchers
This commit is contained in:
parent
e0e7d7d009
commit
7ced1f4278
36 changed files with 938 additions and 707 deletions
|
@ -15,6 +15,7 @@ const HEADER_AUTH_NONCE: &'static str = "WWW-Authenticate";
|
||||||
|
|
||||||
/// The default download count.
|
/// The default download count.
|
||||||
pub const PARAMS_DEFAULT_DOWNLOAD: u8 = 1;
|
pub const PARAMS_DEFAULT_DOWNLOAD: u8 = 1;
|
||||||
|
pub const PARAMS_DEFAULT_DOWNLOAD_STR: &'static str = "1";
|
||||||
|
|
||||||
/// The minimum allowed number of downloads, enforced by the server.
|
/// The minimum allowed number of downloads, enforced by the server.
|
||||||
pub const PARAMS_DOWNLOAD_MIN: u8 = 1;
|
pub const PARAMS_DOWNLOAD_MIN: u8 = 1;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use clap::ArgMatches;
|
||||||
use ffsend_api::action::delete::{
|
use ffsend_api::action::delete::{
|
||||||
Error as DeleteError,
|
Error as DeleteError,
|
||||||
Delete as ApiDelete,
|
Delete as ApiDelete,
|
||||||
|
@ -8,34 +9,40 @@ use ffsend_api::file::remote_file::{
|
||||||
};
|
};
|
||||||
use ffsend_api::reqwest::Client;
|
use ffsend_api::reqwest::Client;
|
||||||
|
|
||||||
use cmd::cmd_delete::CmdDelete;
|
use cmd::matcher::{
|
||||||
|
Matcher,
|
||||||
|
delete::DeleteMatcher,
|
||||||
|
};
|
||||||
use error::ActionError;
|
use error::ActionError;
|
||||||
use util::print_success;
|
use util::print_success;
|
||||||
|
|
||||||
/// A file delete action.
|
/// A file delete action.
|
||||||
pub struct Delete<'a> {
|
pub struct Delete<'a> {
|
||||||
cmd: &'a CmdDelete<'a>,
|
cmd_matches: &'a ArgMatches<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Delete<'a> {
|
impl<'a> Delete<'a> {
|
||||||
/// Construct a new delete action.
|
/// Construct a new delete action.
|
||||||
pub fn new(cmd: &'a CmdDelete<'a>) -> Self {
|
pub fn new(cmd_matches: &'a ArgMatches<'a>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
cmd,
|
cmd_matches,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Invoke the delete action.
|
/// Invoke the delete action.
|
||||||
// TODO: create a trait for this method
|
// TODO: create a trait for this method
|
||||||
pub fn invoke(&self) -> Result<(), ActionError> {
|
pub fn invoke(&self) -> Result<(), ActionError> {
|
||||||
|
// Create the command matchers
|
||||||
|
let matcher_delete = DeleteMatcher::with(self.cmd_matches).unwrap();
|
||||||
|
|
||||||
// Get the share URL
|
// Get the share URL
|
||||||
let url = self.cmd.url();
|
let url = matcher_delete.url();
|
||||||
|
|
||||||
// Create a reqwest client
|
// Create a reqwest client
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
|
|
||||||
// Parse the remote file based on the share URL, get the password
|
// Parse the remote file based on the share URL, get the password
|
||||||
let file = RemoteFile::parse_url(url, self.cmd.owner())?;
|
let file = RemoteFile::parse_url(url, matcher_delete.owner())?;
|
||||||
|
|
||||||
// TODO: show an informative error if the owner token isn't set
|
// TODO: show an informative error if the owner token isn't set
|
||||||
|
|
||||||
|
|
|
@ -1,31 +1,38 @@
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use clap::ArgMatches;
|
||||||
use ffsend_api::action::download::Download as ApiDownload;
|
use ffsend_api::action::download::Download as ApiDownload;
|
||||||
use ffsend_api::file::remote_file::RemoteFile;
|
use ffsend_api::file::remote_file::RemoteFile;
|
||||||
use ffsend_api::reqwest::Client;
|
use ffsend_api::reqwest::Client;
|
||||||
|
|
||||||
use cmd::cmd_download::CmdDownload;
|
use cmd::matcher::{
|
||||||
|
Matcher,
|
||||||
|
download::DownloadMatcher,
|
||||||
|
};
|
||||||
use error::ActionError;
|
use error::ActionError;
|
||||||
use progress::ProgressBar;
|
use progress::ProgressBar;
|
||||||
|
|
||||||
/// A file download action.
|
/// A file download action.
|
||||||
pub struct Download<'a> {
|
pub struct Download<'a> {
|
||||||
cmd: &'a CmdDownload<'a>,
|
cmd_matches: &'a ArgMatches<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Download<'a> {
|
impl<'a> Download<'a> {
|
||||||
/// Construct a new download action.
|
/// Construct a new download action.
|
||||||
pub fn new(cmd: &'a CmdDownload<'a>) -> Self {
|
pub fn new(cmd_matches: &'a ArgMatches<'a>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
cmd,
|
cmd_matches,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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) -> Result<(), ActionError> {
|
pub fn invoke(&self) -> Result<(), ActionError> {
|
||||||
|
// Create the command matchers
|
||||||
|
let matcher_download = DownloadMatcher::with(self.cmd_matches).unwrap();
|
||||||
|
|
||||||
// Get the share URL
|
// Get the share URL
|
||||||
let url = self.cmd.url();
|
let url = matcher_download.url();
|
||||||
|
|
||||||
// Create a reqwest client
|
// Create a reqwest client
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
|
@ -35,7 +42,7 @@ impl<'a> Download<'a> {
|
||||||
let file = RemoteFile::parse_url(url, None)?;
|
let file = RemoteFile::parse_url(url, None)?;
|
||||||
|
|
||||||
// Get the target file or directory
|
// Get the target file or directory
|
||||||
let target = self.cmd.output();
|
let target = matcher_download.output();
|
||||||
|
|
||||||
// Create a progress bar reporter
|
// Create a progress bar reporter
|
||||||
let bar = Arc::new(Mutex::new(ProgressBar::new_download()));
|
let bar = Arc::new(Mutex::new(ProgressBar::new_download()));
|
||||||
|
@ -44,7 +51,7 @@ impl<'a> Download<'a> {
|
||||||
ApiDownload::new(
|
ApiDownload::new(
|
||||||
&file,
|
&file,
|
||||||
target,
|
target,
|
||||||
self.cmd.password(),
|
matcher_download.password(),
|
||||||
).invoke(&client, bar)?;
|
).invoke(&client, bar)?;
|
||||||
|
|
||||||
// TODO: open the file, or it's location
|
// TODO: open the file, or it's location
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use clap::ArgMatches;
|
||||||
use failure::Fail;
|
use failure::Fail;
|
||||||
use ffsend_api::action::exists::{
|
use ffsend_api::action::exists::{
|
||||||
Error as ExistsError,
|
Error as ExistsError,
|
||||||
|
@ -14,34 +15,40 @@ use ffsend_api::file::remote_file::{
|
||||||
};
|
};
|
||||||
use ffsend_api::reqwest::Client;
|
use ffsend_api::reqwest::Client;
|
||||||
|
|
||||||
use cmd::cmd_info::CmdInfo;
|
use cmd::matcher::{
|
||||||
|
Matcher,
|
||||||
|
info::InfoMatcher,
|
||||||
|
};
|
||||||
use util::print_error;
|
use util::print_error;
|
||||||
|
|
||||||
/// A file info action.
|
/// A file info action.
|
||||||
pub struct Info<'a> {
|
pub struct Info<'a> {
|
||||||
cmd: &'a CmdInfo<'a>,
|
cmd_matches: &'a ArgMatches<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Info<'a> {
|
impl<'a> Info<'a> {
|
||||||
/// Construct a new info action.
|
/// Construct a new info action.
|
||||||
pub fn new(cmd: &'a CmdInfo<'a>) -> Self {
|
pub fn new(cmd_matches: &'a ArgMatches<'a>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
cmd,
|
cmd_matches,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Invoke the info action.
|
/// Invoke the info action.
|
||||||
// TODO: create a trait for this method
|
// TODO: create a trait for this method
|
||||||
pub fn invoke(&self) -> Result<(), Error> {
|
pub fn invoke(&self) -> Result<(), Error> {
|
||||||
|
// Create the command matchers
|
||||||
|
let matcher_info = InfoMatcher::with(self.cmd_matches).unwrap();
|
||||||
|
|
||||||
// Get the share URL
|
// Get the share URL
|
||||||
let url = self.cmd.url();
|
let url = matcher_info.url();
|
||||||
|
|
||||||
// Create a reqwest client
|
// Create a reqwest client
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
|
|
||||||
// Parse the remote file based on the share URL, get the password
|
// Parse the remote file based on the share URL, get the password
|
||||||
let file = RemoteFile::parse_url(url, self.cmd.owner())?;
|
let file = RemoteFile::parse_url(url, matcher_info.owner())?;
|
||||||
let password = self.cmd.password();
|
let password = matcher_info.password();
|
||||||
|
|
||||||
// TODO: show an informative error if the owner token isn't set
|
// TODO: show an informative error if the owner token isn't set
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use clap::ArgMatches;
|
||||||
use ffsend_api::action::params::{
|
use ffsend_api::action::params::{
|
||||||
Params as ApiParams,
|
Params as ApiParams,
|
||||||
ParamsDataBuilder,
|
ParamsDataBuilder,
|
||||||
|
@ -5,41 +6,47 @@ use ffsend_api::action::params::{
|
||||||
use ffsend_api::file::remote_file::RemoteFile;
|
use ffsend_api::file::remote_file::RemoteFile;
|
||||||
use ffsend_api::reqwest::Client;
|
use ffsend_api::reqwest::Client;
|
||||||
|
|
||||||
use cmd::cmd_params::CmdParams;
|
use cmd::matcher::{
|
||||||
|
Matcher,
|
||||||
|
params::ParamsMatcher,
|
||||||
|
};
|
||||||
use error::ActionError;
|
use error::ActionError;
|
||||||
use util::print_success;
|
use util::print_success;
|
||||||
|
|
||||||
/// A file parameters action.
|
/// A file parameters action.
|
||||||
pub struct Params<'a> {
|
pub struct Params<'a> {
|
||||||
cmd: &'a CmdParams<'a>,
|
cmd_matches: &'a ArgMatches<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Params<'a> {
|
impl<'a> Params<'a> {
|
||||||
/// Construct a new parameters action.
|
/// Construct a new parameters action.
|
||||||
pub fn new(cmd: &'a CmdParams<'a>) -> Self {
|
pub fn new(cmd_matches: &'a ArgMatches<'a>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
cmd,
|
cmd_matches,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Invoke the parameters action.
|
/// Invoke the parameters action.
|
||||||
// TODO: create a trait for this method
|
// TODO: create a trait for this method
|
||||||
pub fn invoke(&self) -> Result<(), ActionError> {
|
pub fn invoke(&self) -> Result<(), ActionError> {
|
||||||
|
// Create the command matchers
|
||||||
|
let matcher_params = ParamsMatcher::with(self.cmd_matches).unwrap();
|
||||||
|
|
||||||
// Get the share URL
|
// Get the share URL
|
||||||
let url = self.cmd.url();
|
let url = matcher_params.url();
|
||||||
|
|
||||||
// Create a reqwest client
|
// Create a reqwest client
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
|
|
||||||
// Parse the remote file based on the share URL
|
// Parse the remote file based on the share URL
|
||||||
// TODO: handle error here
|
// TODO: handle error here
|
||||||
let file = RemoteFile::parse_url(url, self.cmd.owner())?;
|
let file = RemoteFile::parse_url(url, matcher_params.owner())?;
|
||||||
|
|
||||||
// TODO: show an informative error if the owner token isn't set
|
// TODO: show an informative error if the owner token isn't set
|
||||||
|
|
||||||
// Build the parameters data object
|
// Build the parameters data object
|
||||||
let data = ParamsDataBuilder::default()
|
let data = ParamsDataBuilder::default()
|
||||||
.download_limit(self.cmd.download_limit())
|
.download_limit(matcher_params.download_limit())
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -1,41 +1,48 @@
|
||||||
|
use clap::ArgMatches;
|
||||||
use ffsend_api::action::password::Password as ApiPassword;
|
use ffsend_api::action::password::Password as ApiPassword;
|
||||||
use ffsend_api::file::remote_file::RemoteFile;
|
use ffsend_api::file::remote_file::RemoteFile;
|
||||||
use ffsend_api::reqwest::Client;
|
use ffsend_api::reqwest::Client;
|
||||||
|
|
||||||
use cmd::cmd_password::CmdPassword;
|
use cmd::matcher::{
|
||||||
|
Matcher,
|
||||||
|
password::PasswordMatcher,
|
||||||
|
};
|
||||||
use error::ActionError;
|
use error::ActionError;
|
||||||
use util::print_success;
|
use util::print_success;
|
||||||
|
|
||||||
/// A file password action.
|
/// A file password action.
|
||||||
pub struct Password<'a> {
|
pub struct Password<'a> {
|
||||||
cmd: &'a CmdPassword<'a>,
|
cmd_matches: &'a ArgMatches<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Password<'a> {
|
impl<'a> Password<'a> {
|
||||||
/// Construct a new password action.
|
/// Construct a new password action.
|
||||||
pub fn new(cmd: &'a CmdPassword<'a>) -> Self {
|
pub fn new(cmd_matches: &'a ArgMatches<'a>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
cmd,
|
cmd_matches,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Invoke the password action.
|
/// Invoke the password action.
|
||||||
// TODO: create a trait for this method
|
// TODO: create a trait for this method
|
||||||
pub fn invoke(&self) -> Result<(), ActionError> {
|
pub fn invoke(&self) -> Result<(), ActionError> {
|
||||||
|
// Create the command matchers
|
||||||
|
let matcher_password = PasswordMatcher::with(self.cmd_matches).unwrap();
|
||||||
|
|
||||||
// Get the share URL
|
// Get the share URL
|
||||||
let url = self.cmd.url();
|
let url = matcher_password.url();
|
||||||
|
|
||||||
// Create a reqwest client
|
// Create a reqwest client
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
|
|
||||||
// Parse the remote file based on the share URL
|
// Parse the remote file based on the share URL
|
||||||
// TODO: handle error here
|
// TODO: handle error here
|
||||||
let file = RemoteFile::parse_url(url, self.cmd.owner())?;
|
let file = RemoteFile::parse_url(url, matcher_password.owner())?;
|
||||||
|
|
||||||
// TODO: show an informative error if the owner token isn't set
|
// TODO: show an informative error if the owner token isn't set
|
||||||
|
|
||||||
// Execute an password action
|
// Execute an password action
|
||||||
ApiPassword::new(&file, &self.cmd.password(), None).invoke(&client)?;
|
ApiPassword::new(&file, &matcher_password.password(), None).invoke(&client)?;
|
||||||
|
|
||||||
// Print a success message
|
// Print a success message
|
||||||
print_success("Password set");
|
print_success("Password set");
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use clap::ArgMatches;
|
||||||
use failure::{err_msg, Fail};
|
use failure::{err_msg, Fail};
|
||||||
use ffsend_api::action::params::ParamsDataBuilder;
|
use ffsend_api::action::params::ParamsDataBuilder;
|
||||||
use ffsend_api::action::upload::Upload as ApiUpload;
|
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::matcher::{
|
||||||
|
Matcher,
|
||||||
|
upload::UploadMatcher,
|
||||||
|
};
|
||||||
use error::ActionError;
|
use error::ActionError;
|
||||||
use progress::ProgressBar;
|
use progress::ProgressBar;
|
||||||
use util::open_url;
|
use util::open_url;
|
||||||
|
@ -15,23 +19,26 @@ use util::{print_error, set_clipboard};
|
||||||
|
|
||||||
/// A file upload action.
|
/// A file upload action.
|
||||||
pub struct Upload<'a> {
|
pub struct Upload<'a> {
|
||||||
cmd: &'a CmdUpload<'a>,
|
cmd_matches: &'a ArgMatches<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Upload<'a> {
|
impl<'a> Upload<'a> {
|
||||||
/// Construct a new upload action.
|
/// Construct a new upload action.
|
||||||
pub fn new(cmd: &'a CmdUpload<'a>) -> Self {
|
pub fn new(cmd_matches: &'a ArgMatches<'a>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
cmd,
|
cmd_matches,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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) -> Result<(), ActionError> {
|
pub fn invoke(&self) -> Result<(), ActionError> {
|
||||||
|
// Create the command matchers
|
||||||
|
let matcher_upload = UploadMatcher::with(self.cmd_matches).unwrap();
|
||||||
|
|
||||||
// Get API parameters
|
// Get API parameters
|
||||||
let path = Path::new(self.cmd.file()).to_path_buf();
|
let path = Path::new(matcher_upload.file()).to_path_buf();
|
||||||
let host = self.cmd.host();
|
let host = matcher_upload.host();
|
||||||
|
|
||||||
// Create a reqwest client
|
// Create a reqwest client
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
|
@ -43,7 +50,7 @@ impl<'a> Upload<'a> {
|
||||||
let params = {
|
let params = {
|
||||||
// Build the parameters data object
|
// Build the parameters data object
|
||||||
let mut params = ParamsDataBuilder::default()
|
let mut params = ParamsDataBuilder::default()
|
||||||
.download_limit(self.cmd.download_limit())
|
.download_limit(matcher_upload.download_limit())
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -59,8 +66,8 @@ impl<'a> Upload<'a> {
|
||||||
let file = ApiUpload::new(
|
let file = ApiUpload::new(
|
||||||
host,
|
host,
|
||||||
path,
|
path,
|
||||||
self.cmd.name().map(|name| name.to_owned()),
|
matcher_upload.name().map(|name| name.to_owned()),
|
||||||
self.cmd.password(),
|
matcher_upload.password(),
|
||||||
params,
|
params,
|
||||||
).invoke(&client, bar)?;
|
).invoke(&client, bar)?;
|
||||||
|
|
||||||
|
@ -70,7 +77,7 @@ impl<'a> Upload<'a> {
|
||||||
println!("Owner token: {}", file.owner_token().unwrap());
|
println!("Owner token: {}", file.owner_token().unwrap());
|
||||||
|
|
||||||
// Open the URL in the browser
|
// Open the URL in the browser
|
||||||
if self.cmd.open() {
|
if matcher_upload.open() {
|
||||||
if let Err(err) = open_url(url.clone()) {
|
if let Err(err) = open_url(url.clone()) {
|
||||||
print_error(
|
print_error(
|
||||||
err.context("Failed to open the URL in the browser")
|
err.context("Failed to open the URL in the browser")
|
||||||
|
@ -81,7 +88,7 @@ impl<'a> Upload<'a> {
|
||||||
// Copy the URL in the user's clipboard
|
// Copy the URL in the user's clipboard
|
||||||
#[cfg(feature = "clipboard")]
|
#[cfg(feature = "clipboard")]
|
||||||
{
|
{
|
||||||
if self.cmd.copy() {
|
if matcher_upload.copy() {
|
||||||
if set_clipboard(url.as_str().to_owned()).is_err() {
|
if set_clipboard(url.as_str().to_owned()).is_err() {
|
||||||
print_error(
|
print_error(
|
||||||
err_msg("Failed to copy the URL to the clipboard")
|
err_msg("Failed to copy the URL to the clipboard")
|
||||||
|
|
55
cli/src/cmd/arg/download_limit.rs
Normal file
55
cli/src/cmd/arg/download_limit.rs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
use clap::{Arg, ArgMatches};
|
||||||
|
use ffsend_api::action::params::{
|
||||||
|
PARAMS_DOWNLOAD_MIN as DOWNLOAD_MIN,
|
||||||
|
PARAMS_DOWNLOAD_MAX as DOWNLOAD_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{CmdArg, CmdArgFlag, CmdArgOption};
|
||||||
|
|
||||||
|
/// The download limit argument.
|
||||||
|
pub struct ArgDownloadLimit { }
|
||||||
|
|
||||||
|
impl CmdArg for ArgDownloadLimit {
|
||||||
|
fn name() -> &'static str {
|
||||||
|
"download-limit"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build<'b, 'c>() -> Arg<'b, 'c> {
|
||||||
|
Arg::with_name("download-limit")
|
||||||
|
.long("download-limit")
|
||||||
|
.short("d")
|
||||||
|
.alias("downloads")
|
||||||
|
.alias("download")
|
||||||
|
.alias("down")
|
||||||
|
.alias("dlimit")
|
||||||
|
.alias("limit")
|
||||||
|
.alias("lim")
|
||||||
|
.alias("l")
|
||||||
|
.value_name("COUNT")
|
||||||
|
.help("The file download limit")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CmdArgFlag for ArgDownloadLimit { }
|
||||||
|
|
||||||
|
impl<'a> CmdArgOption<'a> for ArgDownloadLimit {
|
||||||
|
type Value = Option<u8>;
|
||||||
|
|
||||||
|
fn value<'b: 'a>(matches: &'a ArgMatches<'b>) -> Self::Value {
|
||||||
|
// TODO: do not unwrap, report an error
|
||||||
|
Self::value_raw(matches)
|
||||||
|
.map(|d| d.parse::<u8>().expect("invalid download limit"))
|
||||||
|
.and_then(|d| {
|
||||||
|
// Check the download limit bounds
|
||||||
|
if d < DOWNLOAD_MIN || d > DOWNLOAD_MAX {
|
||||||
|
panic!(
|
||||||
|
"invalid download limit, must be between {} and {}",
|
||||||
|
DOWNLOAD_MIN,
|
||||||
|
DOWNLOAD_MAX,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(d)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
53
cli/src/cmd/arg/host.rs
Normal file
53
cli/src/cmd/arg/host.rs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
use clap::{Arg, ArgMatches};
|
||||||
|
use ffsend_api::url::{ParseError, Url};
|
||||||
|
|
||||||
|
use app::SEND_DEF_HOST;
|
||||||
|
use super::{CmdArg, CmdArgOption};
|
||||||
|
use util::quit_error_msg;
|
||||||
|
|
||||||
|
/// The host argument.
|
||||||
|
pub struct ArgHost { }
|
||||||
|
|
||||||
|
impl CmdArg for ArgHost {
|
||||||
|
fn name() -> &'static str {
|
||||||
|
"host"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build<'b, 'c>() -> Arg<'b, 'c> {
|
||||||
|
Arg::with_name("host")
|
||||||
|
.long("host")
|
||||||
|
.short("h")
|
||||||
|
.alias("server")
|
||||||
|
.value_name("URL")
|
||||||
|
.default_value(SEND_DEF_HOST)
|
||||||
|
.help("The remote host to upload to")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> CmdArgOption<'a> for ArgHost {
|
||||||
|
type Value = Url;
|
||||||
|
|
||||||
|
fn value<'b: 'a>(matches: &'a ArgMatches<'b>) -> Self::Value {
|
||||||
|
// Get the URL
|
||||||
|
let url = Self::value_raw(matches).expect("missing host");
|
||||||
|
|
||||||
|
// Parse the URL
|
||||||
|
// TODO: improve these error messages
|
||||||
|
match Url::parse(url) {
|
||||||
|
Ok(url) => url,
|
||||||
|
Err(ParseError::EmptyHost) =>
|
||||||
|
quit_error_msg("Emtpy host given"),
|
||||||
|
Err(ParseError::InvalidPort) =>
|
||||||
|
quit_error_msg("Invalid host port"),
|
||||||
|
Err(ParseError::InvalidIpv4Address) =>
|
||||||
|
quit_error_msg("Invalid IPv4 address in host"),
|
||||||
|
Err(ParseError::InvalidIpv6Address) =>
|
||||||
|
quit_error_msg("Invalid IPv6 address in host"),
|
||||||
|
Err(ParseError::InvalidDomainCharacter) =>
|
||||||
|
quit_error_msg("Host domains contains an invalid character"),
|
||||||
|
Err(ParseError::RelativeUrlWithoutBase) =>
|
||||||
|
quit_error_msg("Host domain doesn't contain a host"),
|
||||||
|
_ => quit_error_msg("The given host is invalid"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
cli/src/cmd/arg/mod.rs
Normal file
49
cli/src/cmd/arg/mod.rs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
pub mod download_limit;
|
||||||
|
pub mod host;
|
||||||
|
pub mod owner;
|
||||||
|
pub mod password;
|
||||||
|
pub mod url;
|
||||||
|
|
||||||
|
// Reexport to arg module
|
||||||
|
pub use self::download_limit::ArgDownloadLimit;
|
||||||
|
pub use self::host::ArgHost;
|
||||||
|
pub use self::owner::ArgOwner;
|
||||||
|
pub use self::password::ArgPassword;
|
||||||
|
pub use self::url::ArgUrl;
|
||||||
|
|
||||||
|
use clap::{Arg, ArgMatches};
|
||||||
|
|
||||||
|
/// A generic trait, for a reusable command argument struct.
|
||||||
|
/// The `CmdArgFlag` and `CmdArgOption` traits further specify what kind of
|
||||||
|
/// argument this is.
|
||||||
|
pub trait CmdArg {
|
||||||
|
/// Get the argument name that is used as main identifier.
|
||||||
|
fn name() -> &'static str;
|
||||||
|
|
||||||
|
/// Build the argument.
|
||||||
|
fn build<'a, 'b>() -> Arg<'a, 'b>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This `CmdArg` specification defines that this argument may be tested as
|
||||||
|
/// flag. This will allow to test whether the flag is present in the given
|
||||||
|
/// matches.
|
||||||
|
pub trait CmdArgFlag: CmdArg {
|
||||||
|
/// Check whether the argument is present in the given matches.
|
||||||
|
fn is_present<'a>(matches: &ArgMatches<'a>) -> bool {
|
||||||
|
matches.is_present(Self::name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This `CmdArg` specification defines that this argument may be tested as
|
||||||
|
/// option. This will allow to fetch the value of the argument.
|
||||||
|
pub trait CmdArgOption<'a>: CmdArg {
|
||||||
|
/// The type of the argument value.
|
||||||
|
type Value;
|
||||||
|
|
||||||
|
/// Get the argument value.
|
||||||
|
fn value<'b: 'a>(matches: &'a ArgMatches<'b>) -> Self::Value;
|
||||||
|
|
||||||
|
/// Get the raw argument value, as a string reference.
|
||||||
|
fn value_raw<'b: 'a>(matches: &'a ArgMatches<'b>) -> Option<&'a str> {
|
||||||
|
matches.value_of(Self::name()) }
|
||||||
|
}
|
31
cli/src/cmd/arg/owner.rs
Normal file
31
cli/src/cmd/arg/owner.rs
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
use clap::{Arg, ArgMatches};
|
||||||
|
|
||||||
|
use super::{CmdArg, CmdArgOption};
|
||||||
|
|
||||||
|
/// The owner argument.
|
||||||
|
pub struct ArgOwner { }
|
||||||
|
|
||||||
|
impl CmdArg for ArgOwner {
|
||||||
|
fn name() -> &'static str {
|
||||||
|
"owner"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build<'b, 'c>() -> Arg<'b, 'c> {
|
||||||
|
Arg::with_name("owner")
|
||||||
|
.long("owner")
|
||||||
|
.short("o")
|
||||||
|
.alias("own")
|
||||||
|
.alias("owner-token")
|
||||||
|
.alias("token")
|
||||||
|
.value_name("TOKEN")
|
||||||
|
.help("Specify the file owner token")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> CmdArgOption<'a> for ArgOwner {
|
||||||
|
type Value = Option<&'a str>;
|
||||||
|
|
||||||
|
fn value<'b: 'a>(matches: &'a ArgMatches<'b>) -> Self::Value {
|
||||||
|
Self::value_raw(matches)
|
||||||
|
}
|
||||||
|
}
|
51
cli/src/cmd/arg/password.rs
Normal file
51
cli/src/cmd/arg/password.rs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
use clap::{Arg, ArgMatches};
|
||||||
|
use rpassword::prompt_password_stderr;
|
||||||
|
|
||||||
|
use super::{CmdArg, CmdArgFlag, CmdArgOption};
|
||||||
|
|
||||||
|
/// The password argument.
|
||||||
|
pub struct ArgPassword { }
|
||||||
|
|
||||||
|
impl CmdArg for ArgPassword {
|
||||||
|
fn name() -> &'static str {
|
||||||
|
"password"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build<'b, 'c>() -> Arg<'b, 'c> {
|
||||||
|
Arg::with_name("password")
|
||||||
|
.long("password")
|
||||||
|
.short("p")
|
||||||
|
.alias("pass")
|
||||||
|
.value_name("PASSWORD")
|
||||||
|
.min_values(0)
|
||||||
|
.max_values(1)
|
||||||
|
.help("Unlock a password protected file")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CmdArgFlag for ArgPassword { }
|
||||||
|
|
||||||
|
impl<'a> CmdArgOption<'a> for ArgPassword {
|
||||||
|
type Value = Option<String>;
|
||||||
|
|
||||||
|
fn value<'b: 'a>(matches: &'a ArgMatches<'b>) -> Self::Value {
|
||||||
|
// The password flag must be present
|
||||||
|
if !Self::is_present(matches) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the password from the argument if set
|
||||||
|
match Self::value_raw(matches) {
|
||||||
|
None => {},
|
||||||
|
p => return p.map(|p| p.into()),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prompt for the password
|
||||||
|
// TODO: don't unwrap/expect
|
||||||
|
// TODO: create utility function for this
|
||||||
|
Some(
|
||||||
|
prompt_password_stderr("Password: ")
|
||||||
|
.expect("failed to read password from stdin")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
49
cli/src/cmd/arg/url.rs
Normal file
49
cli/src/cmd/arg/url.rs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
use clap::{Arg, ArgMatches};
|
||||||
|
use ffsend_api::url::{ParseError, Url};
|
||||||
|
|
||||||
|
use super::{CmdArg, CmdArgOption};
|
||||||
|
use util::quit_error_msg;
|
||||||
|
|
||||||
|
/// The URL argument.
|
||||||
|
pub struct ArgUrl { }
|
||||||
|
|
||||||
|
impl CmdArg for ArgUrl {
|
||||||
|
fn name() -> &'static str {
|
||||||
|
"URL"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build<'b, 'c>() -> Arg<'b, 'c> {
|
||||||
|
Arg::with_name("URL")
|
||||||
|
.required(true)
|
||||||
|
.multiple(false)
|
||||||
|
.help("The share URL")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> CmdArgOption<'a> for ArgUrl {
|
||||||
|
type Value = Url;
|
||||||
|
|
||||||
|
fn value<'b: 'a>(matches: &'a ArgMatches<'b>) -> Self::Value {
|
||||||
|
// Get the URL
|
||||||
|
let url = Self::value_raw(matches).expect("missing URL");
|
||||||
|
|
||||||
|
// Parse the URL
|
||||||
|
// TODO: improve these error messages
|
||||||
|
match Url::parse(url) {
|
||||||
|
Ok(url) => url,
|
||||||
|
Err(ParseError::EmptyHost) =>
|
||||||
|
quit_error_msg("Emtpy host given"),
|
||||||
|
Err(ParseError::InvalidPort) =>
|
||||||
|
quit_error_msg("Invalid host port"),
|
||||||
|
Err(ParseError::InvalidIpv4Address) =>
|
||||||
|
quit_error_msg("Invalid IPv4 address in host"),
|
||||||
|
Err(ParseError::InvalidIpv6Address) =>
|
||||||
|
quit_error_msg("Invalid IPv6 address in host"),
|
||||||
|
Err(ParseError::InvalidDomainCharacter) =>
|
||||||
|
quit_error_msg("Host domains contains an invalid character"),
|
||||||
|
Err(ParseError::RelativeUrlWithoutBase) =>
|
||||||
|
quit_error_msg("Host domain doesn't contain a host"),
|
||||||
|
_ => quit_error_msg("The given host is invalid"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
use ffsend_api::url::{ParseError, Url};
|
use ffsend_api::url::{ParseError, Url};
|
||||||
|
|
||||||
use super::clap::{App, Arg, ArgMatches, SubCommand};
|
use clap::{App, Arg, ArgMatches, SubCommand};
|
||||||
use rpassword::prompt_password_stderr;
|
use rpassword::prompt_password_stderr;
|
||||||
|
|
||||||
use util::quit_error_msg;
|
use util::quit_error_msg;
|
19
cli/src/cmd/cmd/delete.rs
Normal file
19
cli/src/cmd/cmd/delete.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
use clap::{App, SubCommand};
|
||||||
|
|
||||||
|
use cmd::arg::{ArgOwner, ArgUrl, CmdArg};
|
||||||
|
|
||||||
|
/// The delete command definition.
|
||||||
|
pub struct CmdDelete;
|
||||||
|
|
||||||
|
impl CmdDelete {
|
||||||
|
pub fn build<'a, 'b>() -> App<'a, 'b> {
|
||||||
|
SubCommand::with_name("delete")
|
||||||
|
.about("Delete a shared file.")
|
||||||
|
.visible_alias("del")
|
||||||
|
.alias("r")
|
||||||
|
.alias("rem")
|
||||||
|
.alias("remove")
|
||||||
|
.arg(ArgUrl::build())
|
||||||
|
.arg(ArgOwner::build())
|
||||||
|
}
|
||||||
|
}
|
25
cli/src/cmd/cmd/download.rs
Normal file
25
cli/src/cmd/cmd/download.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
use clap::{App, Arg, SubCommand};
|
||||||
|
|
||||||
|
use cmd::arg::{ArgPassword, ArgUrl, CmdArg};
|
||||||
|
|
||||||
|
/// The download command definition.
|
||||||
|
pub struct CmdDownload;
|
||||||
|
|
||||||
|
impl CmdDownload {
|
||||||
|
pub fn build<'a, 'b>() -> App<'a, 'b> {
|
||||||
|
SubCommand::with_name("download")
|
||||||
|
.about("Download files.")
|
||||||
|
.visible_alias("d")
|
||||||
|
.visible_alias("down")
|
||||||
|
.arg(ArgUrl::build())
|
||||||
|
.arg(ArgPassword::build())
|
||||||
|
.arg(Arg::with_name("output")
|
||||||
|
.long("output")
|
||||||
|
.short("o")
|
||||||
|
.alias("output-file")
|
||||||
|
.alias("out")
|
||||||
|
.alias("file")
|
||||||
|
.value_name("PATH")
|
||||||
|
.help("The output file or directory"))
|
||||||
|
}
|
||||||
|
}
|
18
cli/src/cmd/cmd/info.rs
Normal file
18
cli/src/cmd/cmd/info.rs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
use clap::{App, SubCommand};
|
||||||
|
|
||||||
|
use cmd::arg::{ArgOwner, ArgPassword, ArgUrl, CmdArg};
|
||||||
|
|
||||||
|
/// The info command definition.
|
||||||
|
pub struct CmdInfo;
|
||||||
|
|
||||||
|
impl CmdInfo {
|
||||||
|
pub fn build<'a, 'b>() -> App<'a, 'b> {
|
||||||
|
SubCommand::with_name("info")
|
||||||
|
.about("Fetch info about a shared file.")
|
||||||
|
.visible_alias("i")
|
||||||
|
.alias("information")
|
||||||
|
.arg(ArgUrl::build())
|
||||||
|
.arg(ArgOwner::build())
|
||||||
|
.arg(ArgPassword::build())
|
||||||
|
}
|
||||||
|
}
|
14
cli/src/cmd/cmd/mod.rs
Normal file
14
cli/src/cmd/cmd/mod.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
pub mod delete;
|
||||||
|
pub mod download;
|
||||||
|
pub mod info;
|
||||||
|
pub mod params;
|
||||||
|
pub mod password;
|
||||||
|
pub mod upload;
|
||||||
|
|
||||||
|
// Reexport to cmd module
|
||||||
|
pub use self::delete::CmdDelete;
|
||||||
|
pub use self::download::CmdDownload;
|
||||||
|
pub use self::info::CmdInfo;
|
||||||
|
pub use self::params::CmdParams;
|
||||||
|
pub use self::password::CmdPassword;
|
||||||
|
pub use self::upload::CmdUpload;
|
25
cli/src/cmd/cmd/params.rs
Normal file
25
cli/src/cmd/cmd/params.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
use clap::{App, SubCommand};
|
||||||
|
|
||||||
|
use cmd::arg::{ArgDownloadLimit, ArgOwner, ArgUrl, CmdArg};
|
||||||
|
|
||||||
|
/// The params command definition.
|
||||||
|
pub struct CmdParams;
|
||||||
|
|
||||||
|
impl CmdParams {
|
||||||
|
pub fn build<'a, 'b>() -> App<'a, 'b> {
|
||||||
|
// Create a list of parameter arguments, of which one is required
|
||||||
|
let param_args = [
|
||||||
|
ArgDownloadLimit::name(),
|
||||||
|
];
|
||||||
|
|
||||||
|
SubCommand::with_name("parameters")
|
||||||
|
.about("Change parameters of a shared file.")
|
||||||
|
.visible_alias("params")
|
||||||
|
.alias("par")
|
||||||
|
.alias("param")
|
||||||
|
.alias("parameter")
|
||||||
|
.arg(ArgUrl::build())
|
||||||
|
.arg(ArgOwner::build())
|
||||||
|
.arg(ArgDownloadLimit::build().required_unless_one(¶m_args))
|
||||||
|
}
|
||||||
|
}
|
19
cli/src/cmd/cmd/password.rs
Normal file
19
cli/src/cmd/cmd/password.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
use clap::{App, SubCommand};
|
||||||
|
|
||||||
|
use cmd::arg::{ArgOwner, ArgPassword, ArgUrl, CmdArg};
|
||||||
|
|
||||||
|
/// The password command definition.
|
||||||
|
pub struct CmdPassword;
|
||||||
|
|
||||||
|
impl CmdPassword {
|
||||||
|
pub fn build<'a, 'b>() -> App<'a, 'b> {
|
||||||
|
SubCommand::with_name("password")
|
||||||
|
.about("Change the password of a shared file.")
|
||||||
|
.visible_alias("pass")
|
||||||
|
.visible_alias("p")
|
||||||
|
.arg(ArgUrl::build())
|
||||||
|
.arg(ArgPassword::build()
|
||||||
|
.help("Specify a password, do not prompt"))
|
||||||
|
.arg(ArgOwner::build())
|
||||||
|
}
|
||||||
|
}
|
47
cli/src/cmd/cmd/upload.rs
Normal file
47
cli/src/cmd/cmd/upload.rs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
use clap::{App, Arg, SubCommand};
|
||||||
|
use ffsend_api::action::params::{
|
||||||
|
PARAMS_DEFAULT_DOWNLOAD_STR as DOWNLOAD_DEFAULT,
|
||||||
|
};
|
||||||
|
|
||||||
|
use cmd::arg::{ArgDownloadLimit, ArgHost, ArgPassword, CmdArg};
|
||||||
|
|
||||||
|
/// The uplaod command definition.
|
||||||
|
pub struct CmdUpload;
|
||||||
|
|
||||||
|
impl CmdUpload {
|
||||||
|
pub fn build<'a, 'b>() -> App<'a, 'b> {
|
||||||
|
// Build the subcommand
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
let mut cmd = SubCommand::with_name("upload")
|
||||||
|
.about("Upload files.")
|
||||||
|
.visible_alias("u")
|
||||||
|
.visible_alias("up")
|
||||||
|
.arg(Arg::with_name("FILE")
|
||||||
|
.help("The file to upload")
|
||||||
|
.required(true)
|
||||||
|
.multiple(false))
|
||||||
|
.arg(ArgPassword::build())
|
||||||
|
.arg(ArgDownloadLimit::build().default_value(DOWNLOAD_DEFAULT))
|
||||||
|
.arg(ArgHost::build())
|
||||||
|
.arg(Arg::with_name("name")
|
||||||
|
.long("name")
|
||||||
|
.short("n")
|
||||||
|
.alias("file")
|
||||||
|
.alias("f")
|
||||||
|
.value_name("NAME")
|
||||||
|
.help("Rename the file being uploaded"))
|
||||||
|
.arg(Arg::with_name("open")
|
||||||
|
.long("open")
|
||||||
|
.short("o")
|
||||||
|
.help("Open the share link in your browser"));
|
||||||
|
|
||||||
|
// Optional clipboard support
|
||||||
|
#[cfg(feature = "clipboard")] {
|
||||||
|
cmd = cmd.arg(Arg::with_name("copy")
|
||||||
|
.long("copy")
|
||||||
|
.short("c")
|
||||||
|
.help("Copy the share link to your clipboard"));
|
||||||
|
}
|
||||||
|
cmd
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,81 +0,0 @@
|
||||||
use ffsend_api::url::{ParseError, Url};
|
|
||||||
|
|
||||||
use super::clap::{App, Arg, ArgMatches, SubCommand};
|
|
||||||
|
|
||||||
use util::quit_error_msg;
|
|
||||||
|
|
||||||
/// The delete command.
|
|
||||||
pub struct CmdDelete<'a> {
|
|
||||||
matches: &'a ArgMatches<'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a: 'b, 'b> CmdDelete<'a> {
|
|
||||||
/// Build the sub command definition.
|
|
||||||
pub fn build<'y, 'z>() -> App<'y, 'z> {
|
|
||||||
// Build the subcommand
|
|
||||||
let cmd = SubCommand::with_name("delete")
|
|
||||||
.about("Delete a shared file.")
|
|
||||||
.visible_alias("d")
|
|
||||||
.visible_alias("del")
|
|
||||||
.alias("r")
|
|
||||||
.alias("rem")
|
|
||||||
.alias("remove")
|
|
||||||
.arg(Arg::with_name("URL")
|
|
||||||
.help("The share URL")
|
|
||||||
.required(true)
|
|
||||||
.multiple(false))
|
|
||||||
.arg(Arg::with_name("owner")
|
|
||||||
.long("owner")
|
|
||||||
.short("o")
|
|
||||||
.alias("own")
|
|
||||||
.alias("owner-token")
|
|
||||||
.alias("token")
|
|
||||||
.value_name("TOKEN")
|
|
||||||
.help("File owner token"));
|
|
||||||
|
|
||||||
cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse CLI arguments, from the given parent command matches.
|
|
||||||
pub fn parse(parent: &'a ArgMatches<'a>) -> Option<CmdDelete<'a>> {
|
|
||||||
parent.subcommand_matches("delete")
|
|
||||||
.map(|matches| CmdDelete { matches })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the owner token.
|
|
||||||
pub fn owner(&'a self) -> Option<String> {
|
|
||||||
// TODO: validate the owner token if set
|
|
||||||
self.matches.value_of("owner")
|
|
||||||
.map(|token| token.to_owned())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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 {
|
|
||||||
// Get the host
|
|
||||||
let url = self.matches.value_of("URL")
|
|
||||||
.expect("missing URL");
|
|
||||||
|
|
||||||
// Parse the URL
|
|
||||||
// TODO: improve these error messages
|
|
||||||
match Url::parse(url) {
|
|
||||||
Ok(url) => url,
|
|
||||||
Err(ParseError::EmptyHost) =>
|
|
||||||
quit_error_msg("Emtpy host given"),
|
|
||||||
Err(ParseError::InvalidPort) =>
|
|
||||||
quit_error_msg("Invalid host port"),
|
|
||||||
Err(ParseError::InvalidIpv4Address) =>
|
|
||||||
quit_error_msg("Invalid IPv4 address in host"),
|
|
||||||
Err(ParseError::InvalidIpv6Address) =>
|
|
||||||
quit_error_msg("Invalid IPv6 address in host"),
|
|
||||||
Err(ParseError::InvalidDomainCharacter) =>
|
|
||||||
quit_error_msg("Host domains contains an invalid character"),
|
|
||||||
Err(ParseError::RelativeUrlWithoutBase) =>
|
|
||||||
quit_error_msg("Host domain doesn't contain a host"),
|
|
||||||
_ => quit_error_msg("The given host is invalid"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,113 +0,0 @@
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use ffsend_api::url::{ParseError, Url};
|
|
||||||
|
|
||||||
use rpassword::prompt_password_stderr;
|
|
||||||
use super::clap::{App, Arg, ArgMatches, SubCommand};
|
|
||||||
|
|
||||||
use util::quit_error_msg;
|
|
||||||
|
|
||||||
/// The download command.
|
|
||||||
pub struct CmdDownload<'a> {
|
|
||||||
matches: &'a ArgMatches<'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a: 'b, 'b> CmdDownload<'a> {
|
|
||||||
/// Build the sub command definition.
|
|
||||||
pub fn build<'y, 'z>() -> App<'y, 'z> {
|
|
||||||
// Build the subcommand
|
|
||||||
let cmd = SubCommand::with_name("download")
|
|
||||||
.about("Download files.")
|
|
||||||
.visible_alias("d")
|
|
||||||
.visible_alias("down")
|
|
||||||
.arg(Arg::with_name("URL")
|
|
||||||
.help("The share URL")
|
|
||||||
.required(true)
|
|
||||||
.multiple(false))
|
|
||||||
.arg(Arg::with_name("output")
|
|
||||||
.long("output")
|
|
||||||
.short("o")
|
|
||||||
.alias("output-file")
|
|
||||||
.alias("out")
|
|
||||||
.alias("file")
|
|
||||||
.value_name("PATH")
|
|
||||||
.help("The output file or directory"))
|
|
||||||
.arg(Arg::with_name("password")
|
|
||||||
.long("password")
|
|
||||||
.short("p")
|
|
||||||
.alias("pass")
|
|
||||||
.value_name("PASSWORD")
|
|
||||||
.min_values(0)
|
|
||||||
.max_values(1)
|
|
||||||
.help("Unlock a password protected file"));
|
|
||||||
|
|
||||||
cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse CLI arguments, from the given parent command matches.
|
|
||||||
pub fn parse(parent: &'a ArgMatches<'a>) -> Option<CmdDownload<'a>> {
|
|
||||||
parent.subcommand_matches("download")
|
|
||||||
.map(|matches| CmdDownload { matches })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the file share URL, to download the file from.
|
|
||||||
///
|
|
||||||
/// 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 {
|
|
||||||
// Get the host
|
|
||||||
let url = self.matches.value_of("URL")
|
|
||||||
.expect("missing URL");
|
|
||||||
|
|
||||||
// Parse the URL
|
|
||||||
// TODO: improve these error messages
|
|
||||||
match Url::parse(url) {
|
|
||||||
Ok(url) => url,
|
|
||||||
Err(ParseError::EmptyHost) =>
|
|
||||||
quit_error_msg("Emtpy host given"),
|
|
||||||
Err(ParseError::InvalidPort) =>
|
|
||||||
quit_error_msg("Invalid host port"),
|
|
||||||
Err(ParseError::InvalidIpv4Address) =>
|
|
||||||
quit_error_msg("Invalid IPv4 address in host"),
|
|
||||||
Err(ParseError::InvalidIpv6Address) =>
|
|
||||||
quit_error_msg("Invalid IPv6 address in host"),
|
|
||||||
Err(ParseError::InvalidDomainCharacter) =>
|
|
||||||
quit_error_msg("Host domains contains an invalid character"),
|
|
||||||
Err(ParseError::RelativeUrlWithoutBase) =>
|
|
||||||
quit_error_msg("Host domain doesn't contain a host"),
|
|
||||||
_ => quit_error_msg("The given host is invalid"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The target file or directory to download the file to.
|
|
||||||
/// If a directory is given, the file name of the original uploaded file
|
|
||||||
/// will be used.
|
|
||||||
pub fn output(&'a self) -> PathBuf {
|
|
||||||
self.matches.value_of("output")
|
|
||||||
.map(|path| PathBuf::from(path))
|
|
||||||
.unwrap_or(PathBuf::from("./"))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the password.
|
|
||||||
/// `None` is returned if no password was specified.
|
|
||||||
pub fn password(&'a self) -> Option<String> {
|
|
||||||
// Return none if the property was not set
|
|
||||||
if !self.matches.is_present("password") {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the password from the arguments
|
|
||||||
if let Some(password) = self.matches.value_of("password") {
|
|
||||||
return Some(password.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prompt for the password
|
|
||||||
// TODO: don't unwrap/expect
|
|
||||||
// TODO: create utility function for this
|
|
||||||
Some(
|
|
||||||
prompt_password_stderr("Password: ")
|
|
||||||
.expect("failed to read password from stdin")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,109 +0,0 @@
|
||||||
use ffsend_api::url::{ParseError, Url};
|
|
||||||
|
|
||||||
use rpassword::prompt_password_stderr;
|
|
||||||
use super::clap::{App, Arg, ArgMatches, SubCommand};
|
|
||||||
|
|
||||||
use util::quit_error_msg;
|
|
||||||
|
|
||||||
/// The info command.
|
|
||||||
pub struct CmdInfo<'a> {
|
|
||||||
matches: &'a ArgMatches<'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a: 'b, 'b> CmdInfo<'a> {
|
|
||||||
/// Build the sub command definition.
|
|
||||||
pub fn build<'y, 'z>() -> App<'y, 'z> {
|
|
||||||
// Build the subcommand
|
|
||||||
let cmd = SubCommand::with_name("info")
|
|
||||||
.about("Fetch info about a shared file.")
|
|
||||||
.visible_alias("i")
|
|
||||||
.alias("information")
|
|
||||||
.arg(Arg::with_name("URL")
|
|
||||||
.help("The share URL")
|
|
||||||
.required(true)
|
|
||||||
.multiple(false))
|
|
||||||
.arg(Arg::with_name("owner")
|
|
||||||
.long("owner")
|
|
||||||
.short("o")
|
|
||||||
.alias("own")
|
|
||||||
.alias("owner-token")
|
|
||||||
.alias("token")
|
|
||||||
.value_name("TOKEN")
|
|
||||||
.help("File owner token"))
|
|
||||||
.arg(Arg::with_name("password")
|
|
||||||
.long("password")
|
|
||||||
.short("p")
|
|
||||||
.alias("pass")
|
|
||||||
.value_name("PASSWORD")
|
|
||||||
.min_values(0)
|
|
||||||
.max_values(1)
|
|
||||||
.help("Unlock a password protected file"));
|
|
||||||
|
|
||||||
cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse CLI arguments, from the given parent command matches.
|
|
||||||
pub fn parse(parent: &'a ArgMatches<'a>) -> Option<CmdInfo<'a>> {
|
|
||||||
parent.subcommand_matches("info")
|
|
||||||
.map(|matches| CmdInfo { matches })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the owner token.
|
|
||||||
pub fn owner(&'a self) -> Option<String> {
|
|
||||||
// TODO: validate the owner token if set
|
|
||||||
self.matches.value_of("owner")
|
|
||||||
.map(|token| token.to_owned())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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 {
|
|
||||||
// Get the host
|
|
||||||
let url = self.matches.value_of("URL")
|
|
||||||
.expect("missing URL");
|
|
||||||
|
|
||||||
// Parse the URL
|
|
||||||
// TODO: improve these error messages
|
|
||||||
match Url::parse(url) {
|
|
||||||
Ok(url) => url,
|
|
||||||
Err(ParseError::EmptyHost) =>
|
|
||||||
quit_error_msg("Emtpy host given"),
|
|
||||||
Err(ParseError::InvalidPort) =>
|
|
||||||
quit_error_msg("Invalid host port"),
|
|
||||||
Err(ParseError::InvalidIpv4Address) =>
|
|
||||||
quit_error_msg("Invalid IPv4 address in host"),
|
|
||||||
Err(ParseError::InvalidIpv6Address) =>
|
|
||||||
quit_error_msg("Invalid IPv6 address in host"),
|
|
||||||
Err(ParseError::InvalidDomainCharacter) =>
|
|
||||||
quit_error_msg("Host domains contains an invalid character"),
|
|
||||||
Err(ParseError::RelativeUrlWithoutBase) =>
|
|
||||||
quit_error_msg("Host domain doesn't contain a host"),
|
|
||||||
_ => quit_error_msg("The given host is invalid"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the password.
|
|
||||||
/// `None` is returned if no password was specified.
|
|
||||||
pub fn password(&'a self) -> Option<String> {
|
|
||||||
// Return none if the property was not set
|
|
||||||
if !self.matches.is_present("password") {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the password from the arguments
|
|
||||||
if let Some(password) = self.matches.value_of("password") {
|
|
||||||
return Some(password.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prompt for the password
|
|
||||||
// TODO: don't unwrap/expect
|
|
||||||
// TODO: create utility function for this
|
|
||||||
Some(
|
|
||||||
prompt_password_stderr("Password: ")
|
|
||||||
.expect("failed to read password from stdin")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,120 +0,0 @@
|
||||||
use ffsend_api::action::params::{
|
|
||||||
PARAMS_DOWNLOAD_MIN as DOWNLOAD_MIN,
|
|
||||||
PARAMS_DOWNLOAD_MAX as DOWNLOAD_MAX,
|
|
||||||
};
|
|
||||||
use ffsend_api::url::{ParseError, Url};
|
|
||||||
|
|
||||||
use super::clap::{App, Arg, ArgMatches, SubCommand};
|
|
||||||
|
|
||||||
use util::quit_error_msg;
|
|
||||||
|
|
||||||
/// The parameters command.
|
|
||||||
pub struct CmdParams<'a> {
|
|
||||||
matches: &'a ArgMatches<'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a: 'b, 'b> CmdParams<'a> {
|
|
||||||
/// Build the sub command definition.
|
|
||||||
pub fn build<'y, 'z>() -> App<'y, 'z> {
|
|
||||||
// Build a list of data parameter arguments of which one is required
|
|
||||||
let param_args = ["download-limit"];
|
|
||||||
|
|
||||||
// Build the subcommand
|
|
||||||
let cmd = SubCommand::with_name("parameters")
|
|
||||||
.about("Change parameters of a shared file.")
|
|
||||||
.visible_alias("params")
|
|
||||||
.alias("par")
|
|
||||||
.alias("param")
|
|
||||||
.alias("parameter")
|
|
||||||
.arg(Arg::with_name("URL")
|
|
||||||
.help("The share URL")
|
|
||||||
.required(true)
|
|
||||||
.multiple(false))
|
|
||||||
.arg(Arg::with_name("owner")
|
|
||||||
.long("owner")
|
|
||||||
.short("o")
|
|
||||||
.alias("own")
|
|
||||||
.alias("owner-token")
|
|
||||||
.alias("token")
|
|
||||||
.value_name("TOKEN")
|
|
||||||
.help("File owner token"))
|
|
||||||
.arg(Arg::with_name("download-limit")
|
|
||||||
.long("download-limit")
|
|
||||||
.short("d")
|
|
||||||
.alias("downloads")
|
|
||||||
.alias("download")
|
|
||||||
.alias("down")
|
|
||||||
.alias("dlimit")
|
|
||||||
.alias("limit")
|
|
||||||
.alias("lim")
|
|
||||||
.alias("l")
|
|
||||||
.required_unless_one(¶m_args)
|
|
||||||
.value_name("COUNT")
|
|
||||||
.help("Set the download limit parameter"));
|
|
||||||
|
|
||||||
cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse CLI arguments, from the given parent command matches.
|
|
||||||
pub fn parse(parent: &'a ArgMatches<'a>) -> Option<CmdParams<'a>> {
|
|
||||||
parent.subcommand_matches("parameters")
|
|
||||||
.map(|matches| CmdParams { matches })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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 {
|
|
||||||
// Get the host
|
|
||||||
let url = self.matches.value_of("URL")
|
|
||||||
.expect("missing URL");
|
|
||||||
|
|
||||||
// Parse the URL
|
|
||||||
// TODO: improve these error messages
|
|
||||||
match Url::parse(url) {
|
|
||||||
Ok(url) => url,
|
|
||||||
Err(ParseError::EmptyHost) =>
|
|
||||||
quit_error_msg("Emtpy host given"),
|
|
||||||
Err(ParseError::InvalidPort) =>
|
|
||||||
quit_error_msg("Invalid host port"),
|
|
||||||
Err(ParseError::InvalidIpv4Address) =>
|
|
||||||
quit_error_msg("Invalid IPv4 address in host"),
|
|
||||||
Err(ParseError::InvalidIpv6Address) =>
|
|
||||||
quit_error_msg("Invalid IPv6 address in host"),
|
|
||||||
Err(ParseError::InvalidDomainCharacter) =>
|
|
||||||
quit_error_msg("Host domains contains an invalid character"),
|
|
||||||
Err(ParseError::RelativeUrlWithoutBase) =>
|
|
||||||
quit_error_msg("Host domain doesn't contain a host"),
|
|
||||||
_ => quit_error_msg("The given host is invalid"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the owner token.
|
|
||||||
pub fn owner(&'a self) -> Option<String> {
|
|
||||||
// TODO: validate the owner token if set
|
|
||||||
self.matches.value_of("owner")
|
|
||||||
.map(|token| token.to_owned())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the download limit.
|
|
||||||
pub fn download_limit(&'a self) -> Option<u8> {
|
|
||||||
// TODO: do not unwrap, report an error
|
|
||||||
self.matches.value_of("download-limit")
|
|
||||||
.map(|d| d.parse::<u8>().expect("invalid download limit"))
|
|
||||||
.and_then(|d| {
|
|
||||||
// Check the download limit bounds
|
|
||||||
if d < DOWNLOAD_MIN || d > DOWNLOAD_MAX {
|
|
||||||
panic!(
|
|
||||||
"invalid download limit, must be between {} and {}",
|
|
||||||
DOWNLOAD_MIN,
|
|
||||||
DOWNLOAD_MAX,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the value
|
|
||||||
Some(d)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,196 +0,0 @@
|
||||||
use ffsend_api::action::params::{
|
|
||||||
PARAMS_DEFAULT_DOWNLOAD as DOWNLOAD_DEFAULT,
|
|
||||||
PARAMS_DOWNLOAD_MIN as DOWNLOAD_MIN,
|
|
||||||
PARAMS_DOWNLOAD_MAX as DOWNLOAD_MAX,
|
|
||||||
};
|
|
||||||
use ffsend_api::url::{ParseError, Url};
|
|
||||||
|
|
||||||
use rpassword::prompt_password_stderr;
|
|
||||||
use super::clap::{App, Arg, ArgMatches, SubCommand};
|
|
||||||
|
|
||||||
use app::SEND_DEF_HOST;
|
|
||||||
use util::quit_error_msg;
|
|
||||||
|
|
||||||
/// The upload command.
|
|
||||||
pub struct CmdUpload<'a> {
|
|
||||||
matches: &'a ArgMatches<'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a: 'b, 'b> CmdUpload<'a> {
|
|
||||||
/// Build the sub command definition.
|
|
||||||
pub fn build<'y, 'z>() -> App<'y, 'z> {
|
|
||||||
// Build the subcommand
|
|
||||||
#[allow(unused_mut)]
|
|
||||||
let mut cmd = SubCommand::with_name("upload")
|
|
||||||
.about("Upload files.")
|
|
||||||
.visible_alias("u")
|
|
||||||
.visible_alias("up")
|
|
||||||
.arg(Arg::with_name("FILE")
|
|
||||||
.help("The file to upload")
|
|
||||||
.required(true)
|
|
||||||
.multiple(false))
|
|
||||||
.arg(Arg::with_name("name")
|
|
||||||
.long("name")
|
|
||||||
.short("n")
|
|
||||||
.alias("file")
|
|
||||||
.alias("f")
|
|
||||||
.value_name("NAME")
|
|
||||||
.help("Rename the file being uploaded"))
|
|
||||||
.arg(Arg::with_name("password")
|
|
||||||
.long("password")
|
|
||||||
.short("p")
|
|
||||||
.alias("pass")
|
|
||||||
.value_name("PASSWORD")
|
|
||||||
.min_values(0)
|
|
||||||
.max_values(1)
|
|
||||||
.help("Protect the file with a password"))
|
|
||||||
.arg(Arg::with_name("downloads-limit")
|
|
||||||
.long("download-limit")
|
|
||||||
.short("d")
|
|
||||||
.alias("downloads")
|
|
||||||
.alias("download")
|
|
||||||
.alias("down")
|
|
||||||
.alias("dlimit")
|
|
||||||
.alias("limit")
|
|
||||||
.alias("lim")
|
|
||||||
.alias("l")
|
|
||||||
.value_name("COUNT")
|
|
||||||
.default_value("1")
|
|
||||||
.help("Set the download limit"))
|
|
||||||
.arg(Arg::with_name("host")
|
|
||||||
.long("host")
|
|
||||||
.short("h")
|
|
||||||
.alias("server")
|
|
||||||
.value_name("URL")
|
|
||||||
.default_value(SEND_DEF_HOST)
|
|
||||||
.help("The Send host to upload to"))
|
|
||||||
.arg(Arg::with_name("open")
|
|
||||||
.long("open")
|
|
||||||
.short("o")
|
|
||||||
.help("Open the share link in your browser"));
|
|
||||||
|
|
||||||
// Optional clipboard support
|
|
||||||
#[cfg(feature = "clipboard")] {
|
|
||||||
cmd = cmd.arg(Arg::with_name("copy")
|
|
||||||
.long("copy")
|
|
||||||
.short("c")
|
|
||||||
.help("Copy the share link to your clipboard"));
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse CLI arguments, from the given parent command matches.
|
|
||||||
pub fn parse(parent: &'a ArgMatches<'a>) -> Option<CmdUpload<'a>> {
|
|
||||||
parent.subcommand_matches("upload")
|
|
||||||
.map(|matches| CmdUpload { matches })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The the name to use for the uploaded file.
|
|
||||||
/// If no custom name is given, none is returned.
|
|
||||||
// TODO: validate custom names, no path separators
|
|
||||||
// TODO: only allow extension renaming with force flag
|
|
||||||
pub fn name(&'a self) -> Option<&'a str> {
|
|
||||||
// Get the chosen file name
|
|
||||||
let name = self.matches.value_of("name")?;
|
|
||||||
|
|
||||||
// The file name must not be empty
|
|
||||||
if name.trim().is_empty() {
|
|
||||||
// TODO: return an error here
|
|
||||||
panic!("the new name must not be empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the selected file to upload.
|
|
||||||
// TODO: maybe return a file or path instance here
|
|
||||||
pub fn file(&'a self) -> &'a str {
|
|
||||||
self.matches.value_of("FILE")
|
|
||||||
.expect("no file specified to upload")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the host to upload to.
|
|
||||||
///
|
|
||||||
/// 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 {
|
|
||||||
// Get the host
|
|
||||||
let host = self.matches.value_of("host")
|
|
||||||
.expect("missing host");
|
|
||||||
|
|
||||||
// Parse the URL
|
|
||||||
match Url::parse(host) {
|
|
||||||
Ok(url) => url,
|
|
||||||
Err(ParseError::EmptyHost) =>
|
|
||||||
quit_error_msg("Emtpy host given"),
|
|
||||||
Err(ParseError::InvalidPort) =>
|
|
||||||
quit_error_msg("Invalid host port"),
|
|
||||||
Err(ParseError::InvalidIpv4Address) =>
|
|
||||||
quit_error_msg("Invalid IPv4 address in host"),
|
|
||||||
Err(ParseError::InvalidIpv6Address) =>
|
|
||||||
quit_error_msg("Invalid IPv6 address in host"),
|
|
||||||
Err(ParseError::InvalidDomainCharacter) =>
|
|
||||||
quit_error_msg("Host domains contains an invalid character"),
|
|
||||||
Err(ParseError::RelativeUrlWithoutBase) =>
|
|
||||||
quit_error_msg("Host domain doesn't contain a host"),
|
|
||||||
_ => quit_error_msg("The given host is invalid"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check whether to open the file URL in the user's browser.
|
|
||||||
pub fn open(&self) -> bool {
|
|
||||||
self.matches.is_present("open")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check whether to copy the file URL in the user's clipboard.
|
|
||||||
#[cfg(feature = "clipboard")]
|
|
||||||
pub fn copy(&self) -> bool {
|
|
||||||
self.matches.is_present("copy")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the password.
|
|
||||||
/// `None` is returned if no password was specified.
|
|
||||||
pub fn password(&'a self) -> Option<String> {
|
|
||||||
// Return none if the property was not set
|
|
||||||
if !self.matches.is_present("password") {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the password from the arguments
|
|
||||||
if let Some(password) = self.matches.value_of("password") {
|
|
||||||
return Some(password.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prompt for the password
|
|
||||||
// TODO: don't unwrap/expect
|
|
||||||
// TODO: create utility function for this
|
|
||||||
Some(
|
|
||||||
prompt_password_stderr("Password: ")
|
|
||||||
.expect("failed to read password from stdin")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the download limit if set.
|
|
||||||
pub fn download_limit(&'a self) -> Option<u8> {
|
|
||||||
// Get the download limit, or None if not set or default
|
|
||||||
// TODO: do not unwrap, report an error
|
|
||||||
self.matches.value_of("download-limit")
|
|
||||||
.map(|d| d.parse::<u8>().expect("invalid download limit"))
|
|
||||||
.and_then(|d| if d == DOWNLOAD_DEFAULT { None } else { Some(d) })
|
|
||||||
.and_then(|d| {
|
|
||||||
// Check the download limit bounds
|
|
||||||
if d < DOWNLOAD_MIN || d > DOWNLOAD_MAX {
|
|
||||||
panic!(
|
|
||||||
"invalid download limit, must be between {} and {}",
|
|
||||||
DOWNLOAD_MIN,
|
|
||||||
DOWNLOAD_MAX,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the value
|
|
||||||
Some(d)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +1,24 @@
|
||||||
use super::clap::{App, AppSettings, Arg, ArgMatches};
|
use clap::{App, AppSettings, Arg, ArgMatches};
|
||||||
|
|
||||||
use app::*;
|
use app::*;
|
||||||
|
|
||||||
use super::cmd_delete::CmdDelete;
|
use super::matcher::{
|
||||||
use super::cmd_download::CmdDownload;
|
DeleteMatcher,
|
||||||
use super::cmd_info::CmdInfo;
|
DownloadMatcher,
|
||||||
use super::cmd_params::CmdParams;
|
InfoMatcher,
|
||||||
use super::cmd_password::CmdPassword;
|
Matcher,
|
||||||
use super::cmd_upload::CmdUpload;
|
ParamsMatcher,
|
||||||
|
PasswordMatcher,
|
||||||
|
UploadMatcher,
|
||||||
|
};
|
||||||
|
use super::cmd::{
|
||||||
|
CmdDelete,
|
||||||
|
CmdDownload,
|
||||||
|
CmdInfo,
|
||||||
|
CmdParams,
|
||||||
|
CmdPassword,
|
||||||
|
CmdUpload,
|
||||||
|
};
|
||||||
|
|
||||||
/// CLI argument handler.
|
/// CLI argument handler.
|
||||||
pub struct Handler<'a> {
|
pub struct Handler<'a> {
|
||||||
|
@ -22,8 +33,7 @@ impl<'a: 'b, 'b> Handler<'a> {
|
||||||
.version(APP_VERSION)
|
.version(APP_VERSION)
|
||||||
.author(APP_AUTHOR)
|
.author(APP_AUTHOR)
|
||||||
.about(APP_ABOUT)
|
.about(APP_ABOUT)
|
||||||
.global_setting(AppSettings::GlobalVersion)
|
.global_setting(AppSettings::GlobalVersion) .global_setting(AppSettings::VersionlessSubcommands)
|
||||||
.global_setting(AppSettings::VersionlessSubcommands)
|
|
||||||
// TODO: enable below command when it doesn't break `p` anymore.
|
// TODO: enable below command when it doesn't break `p` anymore.
|
||||||
// .global_setting(AppSettings::InferSubcommands)
|
// .global_setting(AppSettings::InferSubcommands)
|
||||||
.arg(Arg::with_name("force")
|
.arg(Arg::with_name("force")
|
||||||
|
@ -59,33 +69,38 @@ impl<'a: 'b, 'b> Handler<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the raw matches.
|
||||||
|
pub fn matches(&'a self) -> &'a ArgMatches {
|
||||||
|
&self.matches
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the delete sub command, if matched.
|
/// Get the delete sub command, if matched.
|
||||||
pub fn delete(&'a self) -> Option<CmdDelete<'a>> {
|
pub fn delete(&'a self) -> Option<DeleteMatcher> {
|
||||||
CmdDelete::parse(&self.matches)
|
DeleteMatcher::with(&self.matches)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the download sub command, if matched.
|
/// Get the download sub command, if matched.
|
||||||
pub fn download(&'a self) -> Option<CmdDownload<'a>> {
|
pub fn download(&'a self) -> Option<DownloadMatcher> {
|
||||||
CmdDownload::parse(&self.matches)
|
DownloadMatcher::with(&self.matches)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the info sub command, if matched.
|
/// Get the info matcher, if that subcommand is entered.
|
||||||
pub fn info(&'a self) -> Option<CmdInfo<'a>> {
|
pub fn info(&'a self) -> Option<InfoMatcher> {
|
||||||
CmdInfo::parse(&self.matches)
|
InfoMatcher::with(&self.matches)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the parameters sub command, if matched.
|
/// Get the parameters sub command, if matched.
|
||||||
pub fn params(&'a self) -> Option<CmdParams<'a>> {
|
pub fn params(&'a self) -> Option<ParamsMatcher> {
|
||||||
CmdParams::parse(&self.matches)
|
ParamsMatcher::with(&self.matches)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the password sub command, if matched.
|
/// Get the password sub command, if matched.
|
||||||
pub fn password(&'a self) -> Option<CmdPassword<'a>> {
|
pub fn password(&'a self) -> Option<PasswordMatcher> {
|
||||||
CmdPassword::parse(&self.matches)
|
PasswordMatcher::with(&self.matches)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the upload sub command, if matched.
|
/// Get the upload sub command, if matched.
|
||||||
pub fn upload(&'a self) -> Option<CmdUpload<'a>> {
|
pub fn upload(&'a self) -> Option<UploadMatcher> {
|
||||||
CmdUpload::parse(&self.matches)
|
UploadMatcher::with(&self.matches)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
39
cli/src/cmd/matcher/delete.rs
Normal file
39
cli/src/cmd/matcher/delete.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
use clap::ArgMatches;
|
||||||
|
use ffsend_api::url::Url;
|
||||||
|
|
||||||
|
use cmd::arg::{ArgOwner, ArgUrl, CmdArgOption};
|
||||||
|
use super::Matcher;
|
||||||
|
|
||||||
|
/// The delete command matcher.
|
||||||
|
pub struct DeleteMatcher<'a> {
|
||||||
|
matches: &'a ArgMatches<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a: 'b, 'b> DeleteMatcher<'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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the owner token.
|
||||||
|
pub fn owner(&'a self) -> Option<String> {
|
||||||
|
// TODO: just return a string reference here?
|
||||||
|
ArgOwner::value(self.matches)
|
||||||
|
.map(|token| token.to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Matcher<'a> for DeleteMatcher<'a> {
|
||||||
|
fn with(matches: &'a ArgMatches) -> Option<Self> {
|
||||||
|
matches.subcommand_matches("delete")
|
||||||
|
.map(|matches|
|
||||||
|
DeleteMatcher {
|
||||||
|
matches,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
49
cli/src/cmd/matcher/download.rs
Normal file
49
cli/src/cmd/matcher/download.rs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use clap::ArgMatches;
|
||||||
|
use ffsend_api::url::Url;
|
||||||
|
|
||||||
|
use cmd::arg::{ArgPassword, ArgUrl, CmdArgOption};
|
||||||
|
use super::Matcher;
|
||||||
|
|
||||||
|
/// The download command matcher.
|
||||||
|
pub struct DownloadMatcher<'a> {
|
||||||
|
matches: &'a ArgMatches<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a: 'b, 'b> DownloadMatcher<'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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the password.
|
||||||
|
/// `None` is returned if no password was specified.
|
||||||
|
pub fn password(&'a self) -> Option<String> {
|
||||||
|
ArgPassword::value(self.matches)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The target file or directory to download the file to.
|
||||||
|
/// If a directory is given, the file name of the original uploaded file
|
||||||
|
/// will be used.
|
||||||
|
pub fn output(&'a self) -> PathBuf {
|
||||||
|
self.matches.value_of("output")
|
||||||
|
.map(|path| PathBuf::from(path))
|
||||||
|
.unwrap_or(PathBuf::from("./"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Matcher<'a> for DownloadMatcher<'a> {
|
||||||
|
fn with(matches: &'a ArgMatches) -> Option<Self> {
|
||||||
|
matches.subcommand_matches("download")
|
||||||
|
.map(|matches|
|
||||||
|
DownloadMatcher {
|
||||||
|
matches,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
46
cli/src/cmd/matcher/info.rs
Normal file
46
cli/src/cmd/matcher/info.rs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
use ffsend_api::url::Url;
|
||||||
|
|
||||||
|
use clap::ArgMatches;
|
||||||
|
|
||||||
|
use cmd::arg::{ArgOwner, ArgPassword, ArgUrl, CmdArgOption};
|
||||||
|
use super::Matcher;
|
||||||
|
|
||||||
|
/// The info command matcher.
|
||||||
|
pub struct InfoMatcher<'a> {
|
||||||
|
matches: &'a ArgMatches<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a: 'b, 'b> InfoMatcher<'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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the owner token.
|
||||||
|
pub fn owner(&'a self) -> Option<String> {
|
||||||
|
// TODO: just return a string reference here?
|
||||||
|
ArgOwner::value(self.matches)
|
||||||
|
.map(|token| token.to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the password.
|
||||||
|
/// `None` is returned if no password was specified.
|
||||||
|
pub fn password(&'a self) -> Option<String> {
|
||||||
|
ArgPassword::value(self.matches)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Matcher<'a> for InfoMatcher<'a> {
|
||||||
|
fn with(matches: &'a ArgMatches) -> Option<Self> {
|
||||||
|
matches.subcommand_matches("info")
|
||||||
|
.map(|matches|
|
||||||
|
InfoMatcher {
|
||||||
|
matches,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
21
cli/src/cmd/matcher/mod.rs
Normal file
21
cli/src/cmd/matcher/mod.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
pub mod delete;
|
||||||
|
pub mod download;
|
||||||
|
pub mod info;
|
||||||
|
pub mod params;
|
||||||
|
pub mod password;
|
||||||
|
pub mod upload;
|
||||||
|
|
||||||
|
// Reexport to matcher module
|
||||||
|
pub use self::delete::DeleteMatcher;
|
||||||
|
pub use self::download::DownloadMatcher;
|
||||||
|
pub use self::info::InfoMatcher;
|
||||||
|
pub use self::params::ParamsMatcher;
|
||||||
|
pub use self::password::PasswordMatcher;
|
||||||
|
pub use self::upload::UploadMatcher;
|
||||||
|
|
||||||
|
use clap::ArgMatches;
|
||||||
|
|
||||||
|
pub trait Matcher<'a>: Sized {
|
||||||
|
// Construct a new matcher instance from these argument matches.
|
||||||
|
fn with(matches: &'a ArgMatches) -> Option<Self>;
|
||||||
|
}
|
44
cli/src/cmd/matcher/params.rs
Normal file
44
cli/src/cmd/matcher/params.rs
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
use clap::ArgMatches;
|
||||||
|
use ffsend_api::url::Url;
|
||||||
|
|
||||||
|
use cmd::arg::{ArgDownloadLimit, ArgOwner, ArgUrl, CmdArgOption};
|
||||||
|
use super::Matcher;
|
||||||
|
|
||||||
|
/// The params command matcher.
|
||||||
|
pub struct ParamsMatcher<'a> {
|
||||||
|
matches: &'a ArgMatches<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a: 'b, 'b> ParamsMatcher<'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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the owner token.
|
||||||
|
pub fn owner(&'a self) -> Option<String> {
|
||||||
|
// TODO: just return a string reference here?
|
||||||
|
ArgOwner::value(self.matches)
|
||||||
|
.map(|token| token.to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the download limit.
|
||||||
|
pub fn download_limit(&'a self) -> Option<u8> {
|
||||||
|
ArgDownloadLimit::value(self.matches)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Matcher<'a> for ParamsMatcher<'a> {
|
||||||
|
fn with(matches: &'a ArgMatches) -> Option<Self> {
|
||||||
|
matches.subcommand_matches("parameters")
|
||||||
|
.map(|matches|
|
||||||
|
ParamsMatcher {
|
||||||
|
matches,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
55
cli/src/cmd/matcher/password.rs
Normal file
55
cli/src/cmd/matcher/password.rs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
use clap::ArgMatches;
|
||||||
|
use ffsend_api::url::Url;
|
||||||
|
use rpassword::prompt_password_stderr;
|
||||||
|
|
||||||
|
use cmd::arg::{ArgOwner, ArgPassword, ArgUrl, CmdArgOption};
|
||||||
|
use super::Matcher;
|
||||||
|
|
||||||
|
/// The password command matcher.
|
||||||
|
pub struct PasswordMatcher<'a> {
|
||||||
|
matches: &'a ArgMatches<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a: 'b, 'b> PasswordMatcher<'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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the owner token.
|
||||||
|
pub fn owner(&'a self) -> Option<String> {
|
||||||
|
// TODO: just return a string reference here?
|
||||||
|
ArgOwner::value(self.matches)
|
||||||
|
.map(|token| token.to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the password.
|
||||||
|
pub fn password(&'a self) -> String {
|
||||||
|
// Get the password, or prompt for it
|
||||||
|
match ArgPassword::value(self.matches) {
|
||||||
|
Some(password) => password,
|
||||||
|
None => {
|
||||||
|
// Prompt for the password
|
||||||
|
// TODO: don't unwrap/expect
|
||||||
|
// TODO: create utility function for this
|
||||||
|
prompt_password_stderr("New password: ")
|
||||||
|
.expect("failed to read password from stdin")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Matcher<'a> for PasswordMatcher<'a> {
|
||||||
|
fn with(matches: &'a ArgMatches) -> Option<Self> {
|
||||||
|
matches.subcommand_matches("password")
|
||||||
|
.map(|matches|
|
||||||
|
PasswordMatcher {
|
||||||
|
matches,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
87
cli/src/cmd/matcher/upload.rs
Normal file
87
cli/src/cmd/matcher/upload.rs
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
use clap::ArgMatches;
|
||||||
|
use ffsend_api::action::params::{
|
||||||
|
PARAMS_DEFAULT_DOWNLOAD as DOWNLOAD_DEFAULT,
|
||||||
|
};
|
||||||
|
use ffsend_api::url::Url;
|
||||||
|
|
||||||
|
use cmd::arg::{ArgDownloadLimit, ArgHost, ArgPassword, CmdArgOption};
|
||||||
|
use super::Matcher;
|
||||||
|
|
||||||
|
/// The upload command matcher.
|
||||||
|
pub struct UploadMatcher<'a> {
|
||||||
|
matches: &'a ArgMatches<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a: 'b, 'b> UploadMatcher<'a> {
|
||||||
|
/// Get the selected file to upload.
|
||||||
|
// TODO: maybe return a file or path instance here
|
||||||
|
pub fn file(&'a self) -> &'a str {
|
||||||
|
self.matches.value_of("FILE")
|
||||||
|
.expect("no file specified to upload")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The the name to use for the uploaded file.
|
||||||
|
/// If no custom name is given, none is returned.
|
||||||
|
// TODO: validate custom names, no path separators
|
||||||
|
// TODO: only allow extension renaming with force flag
|
||||||
|
pub fn name(&'a self) -> Option<&'a str> {
|
||||||
|
// Get the chosen file name
|
||||||
|
let name = self.matches.value_of("name")?;
|
||||||
|
|
||||||
|
// The file name must not be empty
|
||||||
|
if name.trim().is_empty() {
|
||||||
|
// TODO: return an error here
|
||||||
|
panic!("the new name must not be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the host to upload to.
|
||||||
|
///
|
||||||
|
/// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the password.
|
||||||
|
/// `None` is returned if no password was specified.
|
||||||
|
pub fn password(&'a self) -> Option<String> {
|
||||||
|
ArgPassword::value(self.matches)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the download limit.
|
||||||
|
/// If the download limit was the default, `None` is returned to not
|
||||||
|
/// explicitly set it.
|
||||||
|
pub fn download_limit(&'a self) -> Option<u8> {
|
||||||
|
ArgDownloadLimit::value(self.matches)
|
||||||
|
.and_then(|d| match d {
|
||||||
|
DOWNLOAD_DEFAULT => None,
|
||||||
|
d => Some(d),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check whether to open the file URL in the user's browser.
|
||||||
|
pub fn open(&self) -> bool {
|
||||||
|
self.matches.is_present("open")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check whether to copy the file URL in the user's clipboard.
|
||||||
|
#[cfg(feature = "clipboard")]
|
||||||
|
pub fn copy(&self) -> bool {
|
||||||
|
self.matches.is_present("copy")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Matcher<'a> for UploadMatcher<'a> {
|
||||||
|
fn with(matches: &'a ArgMatches) -> Option<Self> {
|
||||||
|
matches.subcommand_matches("upload")
|
||||||
|
.map(|matches|
|
||||||
|
UploadMatcher {
|
||||||
|
matches,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,7 @@
|
||||||
extern crate clap;
|
pub mod arg;
|
||||||
|
pub mod cmd;
|
||||||
pub mod cmd_delete;
|
|
||||||
pub mod cmd_download;
|
|
||||||
pub mod cmd_info;
|
|
||||||
pub mod cmd_params;
|
|
||||||
pub mod cmd_password;
|
|
||||||
pub mod cmd_upload;
|
|
||||||
pub mod handler;
|
pub mod handler;
|
||||||
|
pub mod matcher;
|
||||||
|
|
||||||
// Reexport modules
|
// Reexport modules
|
||||||
pub use self::handler::Handler;
|
pub use self::handler::Handler;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
extern crate clap;
|
||||||
extern crate failure;
|
extern crate failure;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate failure_derive;
|
extern crate failure_derive;
|
||||||
|
@ -38,38 +39,38 @@ fn main() {
|
||||||
/// message.
|
/// message.
|
||||||
fn invoke_action(handler: &Handler) -> Result<(), Error> {
|
fn invoke_action(handler: &Handler) -> Result<(), Error> {
|
||||||
// Match the delete command
|
// Match the delete command
|
||||||
if let Some(cmd) = handler.delete() {
|
if handler.delete().is_some() {
|
||||||
return Delete::new(&cmd).invoke()
|
return Delete::new(handler.matches()).invoke()
|
||||||
.map_err(|err| err.into());
|
.map_err(|err| err.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match the download command
|
// Match the download command
|
||||||
if let Some(cmd) = handler.download() {
|
if handler.download().is_some() {
|
||||||
return Download::new(&cmd).invoke()
|
return Download::new(handler.matches()).invoke()
|
||||||
.map_err(|err| err.into());
|
.map_err(|err| err.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match the info command
|
// Match the info command
|
||||||
if let Some(cmd) = handler.info() {
|
if handler.info().is_some() {
|
||||||
return Info::new(&cmd).invoke()
|
return Info::new(handler.matches()).invoke()
|
||||||
.map_err(|err| err.into());
|
.map_err(|err| err.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match the parameters command
|
// Match the parameters command
|
||||||
if let Some(cmd) = handler.params() {
|
if handler.params().is_some() {
|
||||||
return Params::new(&cmd).invoke()
|
return Params::new(handler.matches()).invoke()
|
||||||
.map_err(|err| err.into());
|
.map_err(|err| err.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match the password command
|
// Match the password command
|
||||||
if let Some(cmd) = handler.password() {
|
if handler.password().is_some() {
|
||||||
return Password::new(&cmd).invoke()
|
return Password::new(handler.matches()).invoke()
|
||||||
.map_err(|err| err.into());
|
.map_err(|err| err.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match the upload command
|
// Match the upload command
|
||||||
if let Some(cmd) = handler.upload() {
|
if handler.upload().is_some() {
|
||||||
return Upload::new(&cmd).invoke()
|
return Upload::new(handler.matches()).invoke()
|
||||||
.map_err(|err| err.into());
|
.map_err(|err| err.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue