summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorConrad Ludgate <conrad.ludgate@truelayer.com>2022-09-09 15:32:46 +0100
committerConrad Ludgate <conrad.ludgate@truelayer.com>2022-09-09 16:01:15 +0100
commit51cd420e7f91a1a558cf676357fff5930a349a8a (patch)
treedf7c3c5a2fd7aa2ed6d909a857c38d95b3013727
parent474170fd901cdec93843f245d2227bd60a428c3d (diff)
render vendor duration
-rw-r--r--Cargo.lock9
-rw-r--r--Cargo.toml1
-rw-r--r--src/command/client/history.rs16
-rw-r--r--src/command/client/search.rs127
4 files changed, 80 insertions, 73 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 3747553c..6a25cb6d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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"
diff --git a/Cargo.toml b/Cargo.toml
index 05c7964a..8ea326de 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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(" "),