diff options
author | Conrad Ludgate <conrad.ludgate@truelayer.com> | 2022-09-09 15:32:46 +0100 |
---|---|---|
committer | Conrad Ludgate <conrad.ludgate@truelayer.com> | 2022-09-09 16:01:15 +0100 |
commit | 51cd420e7f91a1a558cf676357fff5930a349a8a (patch) | |
tree | df7c3c5a2fd7aa2ed6d909a857c38d95b3013727 | |
parent | 474170fd901cdec93843f245d2227bd60a428c3d (diff) |
render vendor duration
-rw-r--r-- | Cargo.lock | 9 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | src/command/client/history.rs | 16 | ||||
-rw-r--r-- | src/command/client/search.rs | 127 |
4 files changed, 80 insertions, 73 deletions
@@ -86,7 +86,6 @@ dependencies = [ "directories", "eyre", "fs-err", - "humantime 2.1.0", "indicatif", "itertools", "log", @@ -568,7 +567,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" dependencies = [ "atty", - "humantime 1.3.0", + "humantime", "log", "regex", "termcolor", @@ -838,12 +837,6 @@ dependencies = [ ] [[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] name = "hyper" version = "0.14.19" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -65,7 +65,6 @@ async-trait = "0.1.49" chrono-english = "0.1.4" cli-table = { version = "0.4", default-features = false } base64 = "0.13.0" -humantime = "2.1.0" crossbeam-channel = "0.5.1" clap = { version = "3.1.18", features = ["derive"] } clap_complete = "3.1.4" diff --git a/src/command/client/history.rs b/src/command/client/history.rs index 805fe4ca..28202bc4 100644 --- a/src/command/client/history.rs +++ b/src/command/client/history.rs @@ -16,6 +16,8 @@ use atuin_client::{ #[cfg(feature = "sync")] use atuin_client::sync; +use super::search::format_duration; + #[derive(Subcommand)] #[clap(infer_subcommands = true)] pub enum Cmd { @@ -92,12 +94,7 @@ pub fn print_list(h: &[History], list_mode: ListMode) { #[allow(clippy::cast_sign_loss)] pub fn print_human_list(w: &mut StdoutLock, h: &[History]) { for h in h.iter().rev() { - let duration = - humantime::format_duration(Duration::from_nanos(std::cmp::max(h.duration, 0) as u64)) - .to_string(); - let duration: Vec<&str> = duration.split(' ').collect(); - let duration = duration[0]; - + let duration = format_duration(Duration::from_nanos(std::cmp::max(h.duration, 0) as u64)); let time = h.timestamp.format("%Y-%m-%d %H:%M:%S"); let cmd = h.command.trim(); @@ -108,12 +105,7 @@ pub fn print_human_list(w: &mut StdoutLock, h: &[History]) { #[allow(clippy::cast_sign_loss)] pub fn print_regular(w: &mut StdoutLock, h: &[History]) { for h in h.iter().rev() { - let duration = - humantime::format_duration(Duration::from_nanos(std::cmp::max(h.duration, 0) as u64)) - .to_string(); - let duration: Vec<&str> = duration.split(' ').collect(); - let duration = duration[0]; - + let duration = format_duration(Duration::from_nanos(std::cmp::max(h.duration, 0) as u64)); let time = h.timestamp.format("%Y-%m-%d %H:%M:%S"); let cmd = h.command.trim(); diff --git a/src/command/client/search.rs b/src/command/client/search.rs index c50c492c..5f0da0ca 100644 --- a/src/command/client/search.rs +++ b/src/command/client/search.rs @@ -1,4 +1,10 @@ -use std::{env, io::stdout, ops::Sub, time::Duration}; +use std::{ + convert::TryFrom, + env, + io::stdout, + ops::{ControlFlow, Sub}, + time::Duration, +}; use chrono::Utc; use clap::Parser; @@ -124,55 +130,75 @@ struct State { context: Context, } +pub fn format_duration(f: Duration) -> String { + fn item(name: &str, value: u64) -> ControlFlow<String> { + if value > 0 { + ControlFlow::Break(format!("{}{}", value, name)) + } else { + ControlFlow::Continue(()) + } + } + + // impl taken and modified from + // https://github.com/tailhook/humantime/blob/master/src/duration.rs#L295-L331 + // Copyright (c) 2016 The humantime Developers + fn fmt(f: Duration) -> ControlFlow<String, ()> { + let secs = f.as_secs(); + let nanos = f.subsec_nanos(); + + let years = secs / 31_557_600; // 365.25d + let year_days = secs % 31_557_600; + let months = year_days / 2_630_016; // 30.44d + let month_days = year_days % 2_630_016; + let days = month_days / 86400; + let day_secs = month_days % 86400; + let hours = day_secs / 3600; + let minutes = day_secs % 3600 / 60; + let seconds = day_secs % 60; + + let millis = nanos / 1_000_000; + + // a difference from our impl than the original is that + // we only care about the most-significant segment of the duration. + // If the item call returns `Break`, then the `?` will early-return. + // This allows for a very consise impl + item("y", years)?; + item("mo", months)?; + item("d", days)?; + item("h", hours)?; + item("m", minutes)?; + item("s", seconds)?; + item("ms", u64::from(millis))?; + ControlFlow::Continue(()) + } + + match fmt(f) { + ControlFlow::Break(b) => b, + ControlFlow::Continue(()) => String::from("0s"), + } +} + +fn duration(h: &History) -> String { + let duration = Duration::from_nanos(u64::try_from(h.duration).unwrap_or(0)); + format_duration(duration) +} + +fn ago(h: &History) -> String { + let ago = chrono::Utc::now().sub(h.timestamp); + + // Account for the chance that h.timestamp is "in the future" + // This would mean that "ago" is negative, and the unwrap here + // would fail. + // If the timestamp would otherwise be in the future, display + // the time ago as 0. + let ago = ago.to_std().unwrap_or_default(); + format_duration(ago) + " ago" +} + impl State { #[allow(clippy::cast_sign_loss)] fn durations(&self) -> Vec<(String, String)> { - self.results - .iter() - .map(|h| { - let duration = - Duration::from_millis(std::cmp::max(h.duration, 0) as u64 / 1_000_000); - let duration = humantime::format_duration(duration).to_string(); - let duration: Vec<&str> = duration.split(' ').collect(); - - let ago = chrono::Utc::now().sub(h.timestamp); - - // Account for the chance that h.timestamp is "in the future" - // This would mean that "ago" is negative, and the unwrap here - // would fail. - // If the timestamp would otherwise be in the future, display - // the time ago as 0. - let ago = humantime::format_duration( - ago.to_std().unwrap_or_else(|_| Duration::new(0, 0)), - ) - .to_string(); - let ago: Vec<&str> = ago.split(' ').collect(); - - ( - duration[0] - .to_string() - .replace("days", "d") - .replace("day", "d") - .replace("weeks", "w") - .replace("week", "w") - .replace("months", "mo") - .replace("month", "mo") - .replace("years", "y") - .replace("year", "y"), - ago[0] - .to_string() - .replace("days", "d") - .replace("day", "d") - .replace("weeks", "w") - .replace("week", "w") - .replace("months", "mo") - .replace("month", "mo") - .replace("years", "y") - .replace("year", "y") - + " ago", - ) - }) - .collect() + self.results.iter().map(|h| (duration(h), ago(h))).collect() } fn render_results<T: tui::backend::Backend>( @@ -195,11 +221,8 @@ impl State { let mut command = Span::raw(command); - let (duration, mut ago) = durations[i].clone(); - - while (duration.len() + ago.len()) < max_length { - ago = format!(" {}", ago); - } + let (mut duration, ago) = durations[i].clone(); + duration = format!("{:width$}", duration, width = max_length - ago.len()); let selected_index = match self.results_state.selected() { None => Span::raw(" "), |