From a6186975b2e8fa07f74a07db90c42878ffca77ff Mon Sep 17 00:00:00 2001 From: timvisee Date: Sun, 29 Apr 2018 23:20:10 +0200 Subject: [PATCH] Render file expiry time and secret in history view --- api/src/file/remote_file.rs | 20 +++++++++++++-- cli/src/action/history.rs | 12 ++++++++- cli/src/util.rs | 51 +++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 3 deletions(-) diff --git a/api/src/file/remote_file.rs b/api/src/file/remote_file.rs index 0b0f6e9..612877b 100644 --- a/api/src/file/remote_file.rs +++ b/api/src/file/remote_file.rs @@ -6,9 +6,8 @@ use url::{ ParseError as UrlParseError, Url, }; -use self::chrono::{DateTime, Utc}; +use self::chrono::{DateTime, Duration, Utc}; use self::regex::Regex; -use time::Duration; use url_serde; use config::SEND_DEFAULT_EXPIRE_TIME; @@ -157,6 +156,23 @@ impl RemoteFile { &self.id } + /// Get the duration the file will expire after, + /// if an expiry time is known. + /// Otherwise `None` is returned. + pub fn expire_duration(&self) -> Option { + self.expire_at.as_ref().map(|time| { + // Get the current time + let now = Utc::now(); + + // Return the duration if not expired, otherwise return zero + if time > &now { + *time - now + } else { + Duration::zero() + } + }) + } + /// Set the time this file will expire at. /// None may be given if the expire time is unknown. pub fn set_expire_at(&mut self, expire_at: Option>) { diff --git a/cli/src/action/history.rs b/cli/src/action/history.rs index 86b4fbb..6b4c60e 100644 --- a/cli/src/action/history.rs +++ b/cli/src/action/history.rs @@ -18,6 +18,7 @@ use history::{ History as HistoryManager, LoadError as HistoryLoadError, }; +use util::format_duration; /// A history action. pub struct History<'a> { @@ -61,14 +62,23 @@ impl<'a> History<'a> { Cell::new("#"), Cell::new("FILE ID"), Cell::new("URL"), + Cell::new("EXPIRY"), ])); // Add an entry for each file for (i, file) in history.files().iter().enumerate() { + // Build the expiry time string + let expiry = match file.expire_duration() { + Some(ref expire) => format_duration(expire), + None => "?".into(), + }; + + // Add the row table.add_row(Row::new(vec![ Cell::new(&format!("{}", i + 1)), Cell::new(file.id()), - Cell::new(file.download_url(false).as_str()), + Cell::new(file.download_url(true).as_str()), + Cell::new(&expiry), ])); } diff --git a/cli/src/util.rs b/cli/src/util.rs index 19afcca..cfa34fc 100644 --- a/cli/src/util.rs +++ b/cli/src/util.rs @@ -21,6 +21,7 @@ use self::colored::*; use failure::{err_msg, Fail}; use ffsend_api::url::Url; use rpassword::prompt_password_stderr; +use time::Duration; use cmd::matcher::MainMatcher; @@ -442,6 +443,56 @@ pub fn format_bytes(bytes: u64) -> String { } } +/// Format the given duration in a human readable format. +/// This method builds 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 +/// +/// Only the two most significant units are returned. +/// If the duration is zero seconds or less `now` is returned. +/// +/// The following time strings may be produced: +/// - `8w6d` +/// - `23h14m` +/// - `9m55s` +/// - `1s` +/// - `now` +pub fn format_duration(duration: &Duration) -> String { + // Get the total number of seconds, return immediately if zero or less + let mut secs = duration.num_seconds(); + if secs <= 0 { + return "now".into(); + } + + // Build a list of time units, define a list for time components + let mut components = Vec::new(); + let units = [ + (1 * 60 * 60 * 24 * 7, "w"), + (1 * 60 * 60 * 24, "d"), + (1 * 60 * 60, "h"), + (1 * 60, "m"), + (1, "s"), + ]; + + // Fill the list of time components based on the units which fit + for unit in units.iter() { + if secs >= unit.0 { + components.push(format!("{}{}", secs / unit.0, unit.1)); + secs %= unit.0; + } + } + + // Show only the two most significant components and join them in a string + components.truncate(2); + components.join("") +} + /// Get the name of the executable that was invoked. pub fn exe_name() -> String { current_exe()