Use human readable format to specify expiry time
This commit is contained in:
parent
e39add8ffd
commit
98150f7f44
4 changed files with 87 additions and 5 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -705,6 +705,7 @@ dependencies = [
|
|||
"pbr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"prettytable-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"qr2term 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rpassword 4.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
|
@ -118,6 +118,7 @@ pathdiff = "0.1"
|
|||
pbr = "1"
|
||||
prettytable-rs = "0.8"
|
||||
qr2term = { version = "0.1", optional = true }
|
||||
regex = "1.3.1"
|
||||
rpassword = "4.0"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
use chrono::Duration;
|
||||
use clap::{Arg, ArgMatches};
|
||||
use failure::Fail;
|
||||
use ffsend_api::api::Version as ApiVersion;
|
||||
use ffsend_api::config::expiry_max;
|
||||
|
||||
use super::{CmdArg, CmdArgFlag, CmdArgOption};
|
||||
use crate::cmd::matcher::MainMatcher;
|
||||
use crate::util::{highlight, prompt_yes, quit};
|
||||
use crate::util::{
|
||||
format_duration, highlight, parse_duration, prompt_yes, quit, quit_error, ErrorHints,
|
||||
};
|
||||
|
||||
/// The download limit argument.
|
||||
pub struct ArgExpiryTime {}
|
||||
|
@ -25,10 +29,13 @@ impl ArgExpiryTime {
|
|||
return Some(expiry);
|
||||
}
|
||||
|
||||
// Define function to format seconds
|
||||
let format_secs = |secs: usize| format_duration(Duration::seconds(secs as i64));
|
||||
|
||||
// Prompt the user the specified expiry time is invalid
|
||||
let allowed_str = allowed
|
||||
.iter()
|
||||
.map(|value| format!("{}", value))
|
||||
.map(|secs| format_secs(*secs))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
eprintln!("The expiry time must be one of: {}", allowed_str);
|
||||
|
@ -44,7 +51,10 @@ impl ArgExpiryTime {
|
|||
// Ask to use closest limit, quit if user cancelled
|
||||
let closest = closest(allowed, expiry);
|
||||
if !prompt_yes(
|
||||
&format!("Would you like to set expiry time to {} instead?", closest),
|
||||
&format!(
|
||||
"Would you like to set expiry time to {} instead?",
|
||||
format_secs(closest)
|
||||
),
|
||||
None,
|
||||
main_matcher,
|
||||
) {
|
||||
|
@ -78,8 +88,13 @@ impl<'a> CmdArgOption<'a> for ArgExpiryTime {
|
|||
type Value = Option<usize>;
|
||||
|
||||
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::<usize>().expect("invalid expiry time"))
|
||||
Self::value_raw(matches).map(|t| match parse_duration(t) {
|
||||
Ok(seconds) => seconds,
|
||||
Err(err) => quit_error(
|
||||
err.context("specified invalid file expiry time"),
|
||||
ErrorHints::default(),
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
65
src/util.rs
65
src/util.rs
|
@ -4,6 +4,7 @@ extern crate colored;
|
|||
extern crate directories;
|
||||
extern crate fs2;
|
||||
extern crate open;
|
||||
extern crate regex;
|
||||
#[cfg(feature = "clipboard-bin")]
|
||||
extern crate which;
|
||||
|
||||
|
@ -38,6 +39,7 @@ use ffsend_api::{
|
|||
reqwest,
|
||||
url::Url,
|
||||
};
|
||||
use regex::Regex;
|
||||
use rpassword::prompt_password_stderr;
|
||||
#[cfg(feature = "clipboard-bin")]
|
||||
use which::which;
|
||||
|
@ -824,6 +826,69 @@ pub fn format_bytes(bytes: u64) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
/// Parse the given duration string from human readable format into seconds.
|
||||
/// This method parses a string of time components to represent the given duration.
|
||||
///
|
||||
/// The following time units are used:
|
||||
/// - `w`: weeks
|
||||
/// - `d`: days
|
||||
/// - `h`: hours
|
||||
/// - `m`: minutes
|
||||
/// - `s`: seconds
|
||||
/// The following time strings can be parsed:
|
||||
/// - `8w6d`
|
||||
/// - `23h14m`
|
||||
/// - `9m55s`
|
||||
/// - `1s1s1s1s1s`
|
||||
pub fn parse_duration(duration: &str) -> Result<usize, ParseDurationError> {
|
||||
// Build a regex to grab time parts
|
||||
let re = Regex::new(r"(?i)([0-9]+)(([a-z]|\s*$))")
|
||||
.expect("failed to compile duration parsing regex");
|
||||
|
||||
// We must find any match
|
||||
if re.find(duration).is_none() {
|
||||
return Err(ParseDurationError::Empty);
|
||||
}
|
||||
|
||||
// Parse each time part, sum it's seconds
|
||||
let mut seconds = 0;
|
||||
for capture in re.captures_iter(duration) {
|
||||
// Parse time value and modifier
|
||||
let number = capture[1]
|
||||
.parse::<usize>()
|
||||
.map_err(ParseDurationError::InvalidValue)?;
|
||||
let modifier = capture[2].trim().to_lowercase();
|
||||
|
||||
// Multiply and sum seconds by modifier
|
||||
seconds += match modifier.as_str() {
|
||||
"" | "s" => number,
|
||||
"m" => number * 60,
|
||||
"h" => number * 60 * 60,
|
||||
"d" => number * 60 * 60 * 24,
|
||||
"w" => number * 60 * 60 * 24 * 7,
|
||||
m => return Err(ParseDurationError::UnknownIdentifier(m.into())),
|
||||
};
|
||||
}
|
||||
|
||||
Ok(seconds)
|
||||
}
|
||||
|
||||
/// Represents a duration parsing error.
|
||||
#[derive(Debug, Fail)]
|
||||
pub enum ParseDurationError {
|
||||
/// The given duration string did not contain any duration part.
|
||||
#[fail(display = "given string did not contain any duration part")]
|
||||
Empty,
|
||||
|
||||
/// A numeric value was invalid.
|
||||
#[fail(display = "duration part has invalid numeric value")]
|
||||
InvalidValue(std::num::ParseIntError),
|
||||
|
||||
/// The given duration string contained an invalid duration modifier.
|
||||
#[fail(display = "duration part has unknown time identifier '{}'", _0)]
|
||||
UnknownIdentifier(String),
|
||||
}
|
||||
|
||||
/// Format the given duration in a human readable format.
|
||||
/// This method builds a string of time components to represent
|
||||
/// the given duration.
|
||||
|
|
Loading…
Reference in a new issue