From 2c2b5b27315b037fec48fcd6486f1ae4a46c4486 Mon Sep 17 00:00:00 2001 From: Cyrix126 Date: Thu, 12 Dec 2024 11:43:40 +0100 Subject: [PATCH] feat: improve hashrate and time display on Status tab --- src/app/panels/middle/status/p2pool.rs | 12 +- src/app/panels/middle/status/processes.rs | 59 +++++-- src/helper/mod.rs | 22 ++- src/helper/p2pool.rs | 26 +-- src/helper/tests.rs | 16 +- src/helper/xrig/xmrig.rs | 9 +- src/helper/xrig/xmrig_proxy.rs | 13 +- src/utils/human.rs | 185 ++++++++++++---------- 8 files changed, 216 insertions(+), 126 deletions(-) diff --git a/src/app/panels/middle/status/p2pool.rs b/src/app/panels/middle/status/p2pool.rs index 49f7f1b..3629a19 100644 --- a/src/app/panels/middle/status/p2pool.rs +++ b/src/app/panels/middle/status/p2pool.rs @@ -234,21 +234,21 @@ impl Status { RichText::new("P2Pool Block Mean").underline().color(BONE), ) .on_hover_text(STATUS_SUBMENU_P2POOL_BLOCK_MEAN); - ui.label(api.p2pool_block_mean.to_string()); + ui.label(api.p2pool_block_mean.display(false)); ui.label( RichText::new("Your P2Pool Share Mean") .underline() .color(BONE), ) .on_hover_text(STATUS_SUBMENU_P2POOL_SHARE_MEAN); - ui.label(p2pool_share_mean.to_string()); + ui.label(p2pool_share_mean.display(false)); ui.label( RichText::new("Your Solo Block Mean") .underline() .color(BONE), ) .on_hover_text(STATUS_SUBMENU_SOLO_BLOCK_MEAN); - ui.label(solo_block_mean.to_string()); + ui.label(solo_block_mean.display(false)); } else { ui.label( RichText::new("Your P2Pool Hashrate") @@ -261,21 +261,21 @@ impl Status { RichText::new("P2Pool Block Mean").underline().color(BONE), ) .on_hover_text(STATUS_SUBMENU_P2POOL_BLOCK_MEAN); - ui.label(api.p2pool_block_mean.to_string()); + ui.label(api.p2pool_block_mean.display(false)); ui.label( RichText::new("Your P2Pool Share Mean") .underline() .color(BONE), ) .on_hover_text(STATUS_SUBMENU_P2POOL_SHARE_MEAN); - ui.label(api.p2pool_share_mean.to_string()); + ui.label(api.p2pool_share_mean.display(false)); ui.label( RichText::new("Your Solo Block Mean") .underline() .color(BONE), ) .on_hover_text(STATUS_SUBMENU_SOLO_BLOCK_MEAN); - ui.label(api.solo_block_mean.to_string()); + ui.label(api.solo_block_mean.display(false)); } }) }); diff --git a/src/app/panels/middle/status/processes.rs b/src/app/panels/middle/status/processes.rs index 1fbc830..ae8d09c 100644 --- a/src/app/panels/middle/status/processes.rs +++ b/src/app/panels/middle/status/processes.rs @@ -1,4 +1,4 @@ -use egui::{ScrollArea, Ui}; +use egui::{Label, ScrollArea, Ui}; use std::sync::{Arc, Mutex}; use crate::app::eframe_impl::ProcessStatesGui; @@ -30,7 +30,7 @@ impl Status { states: &ProcessStatesGui, ) { let width_column = ui.text_style_height(&TextStyle::Body) * 16.0; - let height_column = width_column * 2.5; + let height_column = width_column * 2.7; ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); // let width = ((ui.available_width() / 5.0) - (SPACE * 1.7500)).max(0.0); ScrollArea::vertical().show(ui, |ui| { @@ -121,7 +121,14 @@ fn gupax(ui: &mut Ui, sys: &Arc>) { let sys = sys.lock().unwrap(); ui.label(RichText::new("Uptime").underline().color(BONE)) .on_hover_text(STATUS_GUPAX_UPTIME); - ui.label(sys.gupax_uptime.to_string()); + // put some space for uptime so that when seconds appears every minutes, no label is moved. + ui.add_sized( + [ + 0.0, + (ui.text_style_height(&TextStyle::Body) + ui.spacing().item_spacing.y) * 1.5, + ], + Label::new(sys.gupax_uptime.to_string()), + ); ui.label(RichText::new("Gupaxx CPU").underline().color(BONE)) .on_hover_text(STATUS_GUPAX_CPU_USAGE); ui.label(sys.gupax_cpu_usage.to_string()); @@ -159,7 +166,14 @@ fn p2pool( let api = p2pool_api.lock().unwrap(); ui.label(RichText::new("Uptime").underline().color(BONE)) .on_hover_text(STATUS_P2POOL_UPTIME); - ui.label(format!("{}", api.uptime)); + // put some space for uptime so that when seconds appears every minutes, no label is moved. + ui.add_sized( + [ + 0.0, + (ui.text_style_height(&TextStyle::Body) + ui.spacing().item_spacing.y) * 1.5, + ], + Label::new(api.uptime.display(true)), + ); ui.label(RichText::new("Current Shares").underline().color(BONE)) .on_hover_text(STATUS_P2POOL_CURRENT_SHARES); ui.label(api.sidechain_shares.to_string()); @@ -193,10 +207,7 @@ fn p2pool( .color(BONE), ) .on_hover_text(STATUS_P2POOL_HASHRATE); - ui.label(format!( - "[{} H/s]\n[{} H/s]\n[{} H/s]", - api.hashrate_15m, api.hashrate_1h, api.hashrate_24h - )); + ui.label(&api.hashrate); ui.label(RichText::new("Miners Connected").underline().color(BONE)) .on_hover_text(STATUS_P2POOL_CONNECTIONS); ui.label(format!("{}", api.connections)); @@ -240,17 +251,21 @@ fn xmrig_proxy( let api = xmrig_proxy_api.lock().unwrap(); ui.label(RichText::new("Uptime").underline().color(BONE)) .on_hover_text(STATUS_XMRIG_PROXY_UPTIME); - ui.label(api.uptime.to_string()); + // put some space for uptime so that when seconds appears every minutes, no label is moved. + ui.add_sized( + [ + 0.0, + (ui.text_style_height(&TextStyle::Body) + ui.spacing().item_spacing.y) * 1.5, + ], + Label::new(api.uptime.display(true)), + ); ui.label( RichText::new("Hashrate\n(1m/10m/1h/12h/24h)") .underline() .color(BONE), ) .on_hover_text(STATUS_XMRIG_PROXY_HASHRATE); - ui.label(format!( - "[{} H/s]\n[{} H/s]\n[{} H/s]\n[{} H/s]\n[{} H/s]", - api.hashrate_1m, api.hashrate_10m, api.hashrate_1h, api.hashrate_12h, api.hashrate_24h - )); + ui.label(&api.hashrate); ui.label(format!( "[Accepted: {}]\n[Rejected: {}]", api.accepted, api.rejected @@ -282,7 +297,14 @@ fn xmrig( let api = xmrig_api.lock().unwrap(); ui.label(RichText::new("Uptime").underline().color(BONE)) .on_hover_text(STATUS_XMRIG_UPTIME); - ui.label(api.uptime.to_string()); + // put some space for uptime so that when seconds appears every minutes, no label is moved. + ui.add_sized( + [ + 0.0, + (ui.text_style_height(&TextStyle::Body) + ui.spacing().item_spacing.y) * 1.5, + ], + Label::new(api.uptime.display(true)), + ); ui.label(api.resources.to_string()); ui.label( RichText::new("Hashrate\n(10s/1m/15m)") @@ -407,7 +429,14 @@ fn node(ui: &mut Ui, node_alive: bool, node_api: &Arc>) { let api = node_api.lock().unwrap(); ui.label(RichText::new("Uptime").underline().color(BONE)) .on_hover_text(STATUS_NODE_UPTIME); - ui.label(api.uptime.to_string()); + // put some space for uptime so that when seconds appears every minutes, no label is moved. + ui.add_sized( + [ + 0.0, + (ui.text_style_height(&TextStyle::Body) + ui.spacing().item_spacing.y) * 1.5, + ], + Label::new(api.uptime.display(true)), + ); ui.label(RichText::new("Block Height").underline().color(BONE)) .on_hover_text(STATUS_NODE_BLOCK_HEIGHT); diff --git a/src/helper/mod.rs b/src/helper/mod.rs index b83eb2c..243935f 100644 --- a/src/helper/mod.rs +++ b/src/helper/mod.rs @@ -444,7 +444,7 @@ impl Helper { helper: &Helper, max_threads: u16, ) { - let gupax_uptime = helper.uptime.to_string(); + let gupax_uptime = helper.uptime.display(true); let cpu = &sysinfo.cpus()[0]; let gupax_cpu_usage = format!( "{:.2}%", @@ -793,14 +793,20 @@ fn signal_end( let uptime = HumanTime::into_human(start.elapsed()); info!( "{} Watchdog | Stopped ... Uptime was: [{}], Exit status: [{}]", - process.name, uptime, exit_status + process.name, + uptime.display(false), + exit_status ); // This is written directly into the GUI API, because sometimes the 900ms event loop can't catch it. let name = process.name.to_owned(); if let Err(e) = writeln!( gui_api_output_raw, "{}\n{} stopped | Uptime: [{}] | Exit status: [{}]\n{}\n\n\n\n", - name, HORI_CONSOLE, uptime, exit_status, HORI_CONSOLE + name, + HORI_CONSOLE, + uptime.display(false), + exit_status, + HORI_CONSOLE ) { error!( "{} Watchdog | GUI Uptime/Exit status write failed: {}", @@ -831,14 +837,20 @@ fn signal_end( let uptime = HumanTime::into_human(start.elapsed()); info!( "{} Watchdog | Stopped ... Uptime was: [{}], Exit status: [{}]", - process.name, uptime, exit_status + process.name, + uptime.display(false), + exit_status ); // This is written directly into the GUI API, because sometimes the 900ms event loop can't catch it. let name = process.name.to_owned(); if let Err(e) = writeln!( gui_api_output_raw, "{}\n{} stopped | Uptime: [{}] | Exit status: [{}]\n{}\n\n\n\n", - name, HORI_CONSOLE, uptime, exit_status, HORI_CONSOLE + name, + HORI_CONSOLE, + uptime.display(false), + exit_status, + HORI_CONSOLE ) { error!( "{} Watchdog | GUI Uptime/Exit status write failed: {}", diff --git a/src/helper/p2pool.rs b/src/helper/p2pool.rs index 8e0e2cf..a20ef52 100644 --- a/src/helper/p2pool.rs +++ b/src/helper/p2pool.rs @@ -706,9 +706,10 @@ pub struct PubP2poolApi { pub xmr_day: f64, pub xmr_month: f64, // Local API - pub hashrate_15m: HumanNumber, - pub hashrate_1h: HumanNumber, - pub hashrate_24h: HumanNumber, + pub hashrate: String, + pub hashrate_15m: u64, + pub hashrate_1h: u64, + pub hashrate_24h: u64, pub shares_found: Option, pub average_effort: HumanNumber, pub current_effort: HumanNumber, @@ -766,9 +767,10 @@ impl PubP2poolApi { xmr_hour: 0.0, xmr_day: 0.0, xmr_month: 0.0, - hashrate_15m: HumanNumber::unknown(), - hashrate_1h: HumanNumber::unknown(), - hashrate_24h: HumanNumber::unknown(), + hashrate: HumanNumber::from_hashrate(&[None, None, None]).to_string(), + hashrate_15m: 0, + hashrate_1h: 0, + hashrate_24h: 0, shares_found: None, average_effort: HumanNumber::unknown(), current_effort: HumanNumber::unknown(), @@ -940,9 +942,15 @@ impl PubP2poolApi { // Mutate [PubP2poolApi] with data from a [PrivP2poolLocalApi] and the process output. pub(super) fn update_from_local(public: &mut Self, local: PrivP2poolLocalApi) { *public = Self { - hashrate_15m: HumanNumber::from_u64(local.hashrate_15m), - hashrate_1h: HumanNumber::from_u64(local.hashrate_1h), - hashrate_24h: HumanNumber::from_u64(local.hashrate_24h), + hashrate: HumanNumber::from_hashrate(&[ + Some(local.hashrate_15m), + Some(local.hashrate_1h), + Some(local.hashrate_24h), + ]) + .to_string(), + hashrate_15m: local.hashrate_15m, + hashrate_1h: local.hashrate_1h, + hashrate_24h: local.hashrate_24h, shares_found: Some(local.shares_found), average_effort: HumanNumber::to_percent(local.average_effort), current_effort: HumanNumber::to_percent(local.current_effort), diff --git a/src/helper/tests.rs b/src/helper/tests.rs index 28dd82a..f057112 100644 --- a/src/helper/tests.rs +++ b/src/helper/tests.rs @@ -274,9 +274,9 @@ Uptime = 0h 2m 4s let mut p = public.lock().unwrap(); PubP2poolApi::update_from_local(&mut p, local); println!("AFTER LOCAL: {:#?}", p); - assert_eq!(p.hashrate_15m.to_string(), "10,000"); - assert_eq!(p.hashrate_1h.to_string(), "20,000"); - assert_eq!(p.hashrate_24h.to_string(), "30,000"); + assert_eq!(p.hashrate_15m.to_string(), "10000"); + assert_eq!(p.hashrate_1h.to_string(), "20000"); + assert_eq!(p.hashrate_24h.to_string(), "30000"); assert_eq!( p.shares_found.expect("the value is set").to_string(), "1000" @@ -297,14 +297,14 @@ Uptime = 0h 2m 4s assert_eq!(p.p2pool_hashrate.to_string(), "1.000 MH/s"); assert_eq!(p.miners.to_string(), "1,000"); assert_eq!( - p.solo_block_mean.to_string(), - "5 months\n21 days\n9 hours\n52 minutes" + p.solo_block_mean.display(false), + "5 months, 21 days, 9 hours, 52 minutes" ); assert_eq!( - p.p2pool_block_mean.to_string(), - "3 days\n11 hours\n20 minutes" + p.p2pool_block_mean.display(false), + "3 days, 11 hours, 20 minutes" ); - assert_eq!(p.p2pool_share_mean.to_string(), "8 minutes\n20 seconds"); + assert_eq!(p.p2pool_share_mean.display(false), "8 minutes, 20 seconds"); assert_eq!(p.p2pool_percent.to_string(), "0.040000%"); assert_eq!(p.user_p2pool_percent.to_string(), "2.000000%"); assert_eq!(p.user_monero_percent.to_string(), "0.000800%"); diff --git a/src/helper/xrig/xmrig.rs b/src/helper/xrig/xmrig.rs index 97f2a96..82fa135 100644 --- a/src/helper/xrig/xmrig.rs +++ b/src/helper/xrig/xmrig.rs @@ -793,11 +793,16 @@ impl PubXmrigApi { Some(Some(h)) => *h, _ => 0.0, }; - + let total_hasrate = private + .hashrate + .total + .iter() + .map(|x| x.as_ref().map(|y| *y as u64)) + .collect::>>(); *public = Self { worker_id: private.worker_id, resources: HumanNumber::from_load(private.resources.load_average).to_string(), - hashrate: HumanNumber::from_hashrate(private.hashrate.total).to_string(), + hashrate: HumanNumber::from_hashrate(&total_hasrate).to_string(), diff: Unsigned::from(private.connection.diff as usize).to_string(), accepted: Unsigned::from(private.connection.accepted as usize).to_string(), rejected: Unsigned::from(private.connection.rejected as usize).to_string(), diff --git a/src/helper/xrig/xmrig_proxy.rs b/src/helper/xrig/xmrig_proxy.rs index 711e3e1..34d724a 100644 --- a/src/helper/xrig/xmrig_proxy.rs +++ b/src/helper/xrig/xmrig_proxy.rs @@ -12,7 +12,7 @@ use std::{ }; use tokio::spawn; -use crate::human::HumanTime; +use crate::human::{HumanNumber, HumanTime}; use crate::miscs::client; use crate::{ GUPAX_VERSION_UNDERSCORE, UNKNOWN_DATA, @@ -467,12 +467,14 @@ impl Helper { // sleep } } +#[allow(unused)] #[derive(Debug, Clone)] pub struct PubXmrigProxyApi { pub output: String, pub uptime: HumanTime, pub accepted: u32, pub rejected: u32, + pub hashrate: String, pub hashrate_1m: f32, pub hashrate_10m: f32, pub hashrate_1h: f32, @@ -493,6 +495,7 @@ impl PubXmrigProxyApi { uptime: HumanTime::new(), accepted: 0, rejected: 0, + hashrate: HumanNumber::from_hashrate(&[None, None, None, None, None, None]).to_string(), hashrate_1m: 0.0, hashrate_10m: 0.0, hashrate_1h: 0.0, @@ -555,9 +558,17 @@ impl PubXmrigProxyApi { } fn update_from_priv(public: &Arc>, private: PrivXmrigProxyApi) { let mut public = public.lock().unwrap(); + let mut total_hashrate = private + .hashrate + .total + .iter() + .map(|x| Some(*x as u64)) + .collect::>>(); + total_hashrate.remove(5); *public = Self { accepted: private.results.accepted, rejected: private.results.rejected, + hashrate: HumanNumber::from_hashrate(&total_hashrate).to_string(), hashrate_1m: private.hashrate.total[0], hashrate_10m: private.hashrate.total[1], hashrate_1h: private.hashrate.total[2], diff --git a/src/utils/human.rs b/src/utils/human.rs index 84e80fc..8bdcdcb 100644 --- a/src/utils/human.rs +++ b/src/utils/human.rs @@ -26,6 +26,8 @@ pub const ZERO_SECONDS: std::time::Duration = std::time::Duration::from_secs(0); // Code taken from [https://docs.rs/humantime/] and edited to remove sub-second time, change spacing and some words. use std::time::Duration; +use readable::num::Float; + #[derive(Debug, Clone, Eq, PartialEq)] pub struct HumanTime(Duration); @@ -51,34 +53,29 @@ impl HumanTime { HumanTime(Duration::from_secs(u)) } - fn plural( - f: &mut std::fmt::Formatter, - started: &mut bool, - name: &str, - value: u64, - ) -> std::fmt::Result { + fn plural(started: &mut bool, name: &str, value: u64, separator: &str) -> String { + // do not show time if value is 0 unless it is for seconds. + let mut string = String::new(); if value > 0 { if *started { - f.write_str("\n")?; + string.push_str(separator); } - write!(f, "{} {}", value, name)?; + string.push_str(&value.to_string()); + string.push(' '); + string.push_str(name); if value > 1 { - f.write_str("s")?; + string.push('s'); } *started = true; } - Ok(()) + string } -} - -impl std::fmt::Display for HumanTime { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + pub fn display(&self, wrap: bool) -> String { let secs = self.0.as_secs(); if secs == 0 { - f.write_str("0 seconds")?; - return Ok(()); + return String::from("0 second"); } - + let separator = if wrap { "\n" } else { ", " }; let years = secs / 31_557_600; // 365.25d let ydays = secs % 31_557_600; let months = ydays / 2_630_016; // 30.44d @@ -86,17 +83,20 @@ impl std::fmt::Display for HumanTime { let days = mdays / 86400; let day_secs = mdays % 86400; let hours = day_secs / 3600; + dbg!(&day_secs); let minutes = day_secs % 3600 / 60; + dbg!(&minutes); let seconds = day_secs % 60; - - let started = &mut false; - Self::plural(f, started, "year", years)?; - Self::plural(f, started, "month", months)?; - Self::plural(f, started, "day", days)?; - Self::plural(f, started, "hour", hours)?; - Self::plural(f, started, "minute", minutes)?; - Self::plural(f, started, "second", seconds)?; - Ok(()) + dbg!(&seconds); + let mut started = false; + let mut string = String::new(); + string.push_str(&Self::plural(&mut started, "year", years, separator)); + string.push_str(&Self::plural(&mut started, "month", months, separator)); + string.push_str(&Self::plural(&mut started, "day", days, separator)); + string.push_str(&Self::plural(&mut started, "hour", hours, separator)); + string.push_str(&Self::plural(&mut started, "minute", minutes, separator)); + string.push_str(&Self::plural(&mut started, "second", seconds, separator)); + string } } @@ -184,26 +184,36 @@ impl HumanNumber { Self(buf.as_str().to_string()) } #[inline] - pub fn from_hashrate(array: [Option; 3]) -> Self { - let mut string = "[".to_string(); - let mut buf = num_format::Buffer::new(); + pub fn from_hashrate(array: &[Option]) -> Self { + let mut string = String::new(); + // let mut buf = num_format::Buffer::new(); - let mut n = 0; + let mut n = 1; for i in array { match i { Some(f) => { - let f = f as u128; - buf.write_formatted(&f, &LOCALE); - string.push_str(buf.as_str()); - string.push_str(" H/s"); + if *f == 0 { + string.push_str("[??? H/s]"); + } else { + let f = *f as f64; + let (value, metric) = match f { + x if x >= 1000.0 => (Float::from_3(f / 1000.0), " K"), + x if x >= 1000000.0 => (Float::from_3(f / (1000.0 * 1000.0)), " M"), + _ => (Float::from_0(f), " "), + }; + string.push('['); + // buf.write_formatted(&value, &LOCALE); + string.push_str(value.as_str()); + string.push_str(metric); + string.push_str("H/s]"); + } } - None => string.push_str("??? H/s"), + None => string.push_str("[??? H/s]"), } - if n != 2 { - string.push_str(", "); + if n != array.len() { + string.push('\n'); n += 1; } else { - string.push(']'); break; } } @@ -263,13 +273,14 @@ mod test { assert!(HumanNumber::to_percent(0.001).to_string() == "0%"); assert!(HumanNumber::to_percent(12.123_123).to_string() == "12.12%"); assert!(HumanNumber::to_percent_3_point(0.001).to_string() == "0.001%"); + dbg!(HumanNumber::from_hashrate(&[Some(123), Some(11111), None]).to_string()); assert!( - HumanNumber::from_hashrate([Some(123.1), Some(11111.1), None]).to_string() - == "[123 H/s, 11,111 H/s, ??? H/s]" + HumanNumber::from_hashrate(&[Some(123), Some(11111), None]).to_string() + == "[123 H/s]\n[11.111 KH/s]\n[??? H/s]" ); assert!( - HumanNumber::from_hashrate([None, Some(1.123), Some(123_123.31)]).to_string() - == "[??? H/s, 1 H/s, 123,123 H/s]" + HumanNumber::from_hashrate(&[None, Some(1), Some(123_123)]).to_string() + == "[??? H/s]\n[1 H/s]\n[123.123 KH/s]" ); assert!( HumanNumber::from_load([Some(123.1234), Some(321.321), None]).to_string() @@ -327,69 +338,83 @@ mod test { fn human_time() { use crate::human::HumanTime; use std::time::Duration; - assert!(HumanTime::into_human(Duration::from_secs(0)).to_string() == "0 seconds"); - assert!(HumanTime::into_human(Duration::from_secs(1)).to_string() == "1 second"); - assert!(HumanTime::into_human(Duration::from_secs(2)).to_string() == "2 seconds"); - assert!(HumanTime::into_human(Duration::from_secs(59)).to_string() == "59 seconds"); - assert!(HumanTime::into_human(Duration::from_secs(60)).to_string() == "1 minute"); - assert!(HumanTime::into_human(Duration::from_secs(61)).to_string() == "1 minute\n1 second"); + assert!(HumanTime::into_human(Duration::from_secs(0)).display(true) == "0 second"); + assert!(HumanTime::into_human(Duration::from_secs(1)).display(true) == "1 second"); + assert!(HumanTime::into_human(Duration::from_secs(2)).display(true) == "2 seconds"); + assert!(HumanTime::into_human(Duration::from_secs(59)).display(true) == "59 seconds"); + dbg!(HumanTime::into_human(Duration::from_secs(60)).display(true)); + assert!(HumanTime::into_human(Duration::from_secs(60)).display(true) == "1 minute"); assert!( - HumanTime::into_human(Duration::from_secs(62)).to_string() == "1 minute\n2 seconds" - ); - assert!(HumanTime::into_human(Duration::from_secs(120)).to_string() == "2 minutes"); - assert!( - HumanTime::into_human(Duration::from_secs(121)).to_string() == "2 minutes\n1 second" + HumanTime::into_human(Duration::from_secs(61)).display(true) == "1 minute\n1 second" ); assert!( - HumanTime::into_human(Duration::from_secs(122)).to_string() == "2 minutes\n2 seconds" + HumanTime::into_human(Duration::from_secs(62)).display(true) == "1 minute\n2 seconds" + ); + assert!(HumanTime::into_human(Duration::from_secs(120)).display(true) == "2 minutes"); + assert!( + HumanTime::into_human(Duration::from_secs(121)).display(true) == "2 minutes\n1 second" ); assert!( - HumanTime::into_human(Duration::from_secs(179)).to_string() == "2 minutes\n59 seconds" + HumanTime::into_human(Duration::from_secs(122)).display(true) == "2 minutes\n2 seconds" ); assert!( - HumanTime::into_human(Duration::from_secs(3599)).to_string() + HumanTime::into_human(Duration::from_secs(179)).display(true) + == "2 minutes\n59 seconds" + ); + assert!( + HumanTime::into_human(Duration::from_secs(3599)).display(true) == "59 minutes\n59 seconds" ); - assert!(HumanTime::into_human(Duration::from_secs(3600)).to_string() == "1 hour"); - assert!(HumanTime::into_human(Duration::from_secs(3601)).to_string() == "1 hour\n1 second"); + assert!(HumanTime::into_human(Duration::from_secs(3600)).display(true) == "1 hour"); assert!( - HumanTime::into_human(Duration::from_secs(3602)).to_string() == "1 hour\n2 seconds" - ); - assert!(HumanTime::into_human(Duration::from_secs(3660)).to_string() == "1 hour\n1 minute"); - assert!( - HumanTime::into_human(Duration::from_secs(3720)).to_string() == "1 hour\n2 minutes" + HumanTime::into_human(Duration::from_secs(3601)).display(true) == "1 hour\n1 second" ); assert!( - HumanTime::into_human(Duration::from_secs(86399)).to_string() + HumanTime::into_human(Duration::from_secs(3602)).display(true) == "1 hour\n2 seconds" + ); + assert!( + HumanTime::into_human(Duration::from_secs(3660)).display(true) == "1 hour\n1 minute" + ); + assert!( + HumanTime::into_human(Duration::from_secs(3720)).display(true) == "1 hour\n2 minutes" + ); + assert!( + HumanTime::into_human(Duration::from_secs(86399)).display(true) == "23 hours\n59 minutes\n59 seconds" ); - assert!(HumanTime::into_human(Duration::from_secs(86400)).to_string() == "1 day"); - assert!(HumanTime::into_human(Duration::from_secs(86401)).to_string() == "1 day\n1 second"); + assert!(HumanTime::into_human(Duration::from_secs(86400)).display(true) == "1 day"); assert!( - HumanTime::into_human(Duration::from_secs(86402)).to_string() == "1 day\n2 seconds" + HumanTime::into_human(Duration::from_secs(86401)).display(true) == "1 day\n1 second" ); - assert!(HumanTime::into_human(Duration::from_secs(86460)).to_string() == "1 day\n1 minute"); assert!( - HumanTime::into_human(Duration::from_secs(86520)).to_string() == "1 day\n2 minutes" + HumanTime::into_human(Duration::from_secs(86402)).display(true) == "1 day\n2 seconds" ); - assert!(HumanTime::into_human(Duration::from_secs(90000)).to_string() == "1 day\n1 hour"); - assert!(HumanTime::into_human(Duration::from_secs(93600)).to_string() == "1 day\n2 hours"); assert!( - HumanTime::into_human(Duration::from_secs(604799)).to_string() + HumanTime::into_human(Duration::from_secs(86460)).display(true) == "1 day\n1 minute" + ); + assert!( + HumanTime::into_human(Duration::from_secs(86520)).display(true) == "1 day\n2 minutes" + ); + assert!(HumanTime::into_human(Duration::from_secs(90000)).display(true) == "1 day\n1 hour"); + assert!( + HumanTime::into_human(Duration::from_secs(93600)).display(true) == "1 day\n2 hours" + ); + assert!( + HumanTime::into_human(Duration::from_secs(604799)).display(true) == "6 days\n23 hours\n59 minutes\n59 seconds" ); - assert!(HumanTime::into_human(Duration::from_secs(604800)).to_string() == "7 days"); - assert!(HumanTime::into_human(Duration::from_secs(2630016)).to_string() == "1 month"); + assert!(HumanTime::into_human(Duration::from_secs(604800)).display(true) == "7 days"); + assert!(HumanTime::into_human(Duration::from_secs(2630016)).display(true) == "1 month"); assert!( - HumanTime::into_human(Duration::from_secs(3234815)).to_string() + HumanTime::into_human(Duration::from_secs(3234815)).display(true) == "1 month\n6 days\n23 hours\n59 minutes\n59 seconds" ); - assert!(HumanTime::into_human(Duration::from_secs(5260032)).to_string() == "2 months"); - assert!(HumanTime::into_human(Duration::from_secs(31557600)).to_string() == "1 year"); - assert!(HumanTime::into_human(Duration::from_secs(63115200)).to_string() == "2 years"); + assert!(HumanTime::into_human(Duration::from_secs(5260032)).display(true) == "2 months"); + assert!(HumanTime::into_human(Duration::from_secs(31557600)).display(true) == "1 year"); + assert!(HumanTime::into_human(Duration::from_secs(63115200)).display(true) == "2 years"); assert_eq!( - HumanTime::into_human(Duration::from_secs(18446744073709551615)).to_string(), - "584542046090 years\n7 months\n15 days\n17 hours\n5 minutes\n3 seconds", + HumanTime::into_human(Duration::from_secs(18446744073709551615)).display(true), + "584542046090 years\n7 months\n15 days\n17 hours\n5 minutes\n3 seconds" ); } }