From b7f95143401c723db9071e8ef91bad8542ff249b Mon Sep 17 00:00:00 2001 From: Rudolf B <5439317+Rudi3@users.noreply.github.com> Date: Mon, 7 Oct 2019 21:12:26 +0200 Subject: [PATCH] Fix/improve human-readable units --- src/cli/download_tracker.rs | 50 ++++-------- src/diskio/threaded.rs | 5 +- src/utils/mod.rs | 1 + src/utils/notifications.rs | 15 ++-- src/utils/units.rs | 156 ++++++++++++++++++++++++++++++++++++ 5 files changed, 184 insertions(+), 43 deletions(-) create mode 100644 src/utils/units.rs diff --git a/src/cli/download_tracker.rs b/src/cli/download_tracker.rs index dacc12c43f..04459c3c40 100644 --- a/src/cli/download_tracker.rs +++ b/src/cli/download_tracker.rs @@ -1,6 +1,7 @@ use crate::term2; use rustup::dist::Notification as In; use rustup::utils::tty; +use rustup::utils::units::{Size, Unit, UnitMode}; use rustup::utils::Notification as Un; use rustup::Notification; use std::collections::VecDeque; @@ -43,7 +44,7 @@ pub struct DownloadTracker { /// rendered, so we can erase it cleanly. displayed_charcount: Option, /// What units to show progress in - units: Vec, + units: Vec, /// Whether we display progress display_progress: bool, } @@ -60,7 +61,7 @@ impl DownloadTracker { last_sec: None, term: term2::stdout(), displayed_charcount: None, - units: vec!["B".into(); 1], + units: vec![Unit::B], display_progress: true, } } @@ -87,12 +88,12 @@ impl DownloadTracker { self.download_finished(); true } - Notification::Install(In::Utils(Un::DownloadPushUnits(units))) => { - self.push_units(units.into()); + Notification::Install(In::Utils(Un::DownloadPushUnit(unit))) => { + self.push_unit(unit); true } - Notification::Install(In::Utils(Un::DownloadPopUnits)) => { - self.pop_units(); + Notification::Install(In::Utils(Un::DownloadPopUnit)) => { + self.pop_unit(); true } @@ -162,12 +163,12 @@ impl DownloadTracker { /// Display the tracked download information to the terminal. fn display(&mut self) { // Panic if someone pops the default bytes unit... - let units = &self.units.last().unwrap(); - let total_h = Size(self.total_downloaded, units); + let unit = *self.units.last().unwrap(); + let total_h = Size::new(self.total_downloaded, unit, UnitMode::Norm); let sum: usize = self.downloaded_last_few_secs.iter().sum(); let len = self.downloaded_last_few_secs.len(); let speed = if len > 0 { sum / len } else { 0 }; - let speed_h = Size(speed, units); + let speed_h = Size::new(speed, unit, UnitMode::Rate); let elapsed_h = Duration(precise_time_s() - self.start_sec); // First, move to the start of the current line and clear it. @@ -187,18 +188,18 @@ impl DownloadTracker { let output = match self.content_len { Some(content_len) => { - let content_len_h = Size(content_len, units); + let content_len_h = Size::new(content_len, unit, UnitMode::Norm); let content_len = content_len as f64; let percent = (self.total_downloaded as f64 / content_len) * 100.; let remaining = content_len - self.total_downloaded as f64; let eta_h = Duration(remaining / speed as f64); format!( - "{} / {} ({:3.0} %) {}/s in {} ETA: {}", + "{} / {} ({:3.0} %) {} in {} ETA: {}", total_h, content_len_h, percent, speed_h, elapsed_h, eta_h ) } None => format!( - "Total: {} Speed: {}/s Elapsed: {}", + "Total: {} Speed: {} Elapsed: {}", total_h, speed_h, elapsed_h ), }; @@ -209,11 +210,11 @@ impl DownloadTracker { self.displayed_charcount = Some(output.chars().count()); } - pub fn push_units(&mut self, new_units: String) { - self.units.push(new_units); + pub fn push_unit(&mut self, new_unit: Unit) { + self.units.push(new_unit); } - pub fn pop_units(&mut self) { + pub fn pop_unit(&mut self) { self.units.pop(); } } @@ -240,25 +241,6 @@ impl fmt::Display for Duration { } } -/// Human readable size (some units) -struct Size<'a>(usize, &'a str); - -impl<'a> fmt::Display for Size<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - const KI: f64 = 1024.0; - const MI: f64 = KI * KI; - let size = self.0 as f64; - - if size >= MI { - write!(f, "{:5.1} Mi{}", size / MI, self.1) // XYZ.P Mi - } else if size >= KI { - write!(f, "{:5.1} Ki{}", size / KI, self.1) - } else { - write!(f, "{:3.0} {}", size, self.1) - } - } -} - #[cfg(test)] mod tests { #[test] diff --git a/src/diskio/threaded.rs b/src/diskio/threaded.rs index 777af7dea5..b71b2ee78b 100644 --- a/src/diskio/threaded.rs +++ b/src/diskio/threaded.rs @@ -6,6 +6,7 @@ /// will cause minutes of wall clock time. use super::{perform, Executor, Item}; use crate::utils::notifications::Notification; +use crate::utils::units::Unit; use std::cell::Cell; use std::sync::atomic::{AtomicUsize, Ordering}; @@ -118,7 +119,7 @@ impl<'a> Executor for Threaded<'a> { let mut prev_files = self.n_files.load(Ordering::Relaxed); if let Some(handler) = self.notify_handler { handler(Notification::DownloadFinished); - handler(Notification::DownloadPushUnits("iops")); + handler(Notification::DownloadPushUnit(Unit::IO)); handler(Notification::DownloadContentLengthReceived( prev_files as u64, )); @@ -144,7 +145,7 @@ impl<'a> Executor for Threaded<'a> { self.pool.join(); if let Some(handler) = self.notify_handler { handler(Notification::DownloadFinished); - handler(Notification::DownloadPopUnits); + handler(Notification::DownloadPopUnit); } // close the feedback channel so that blocking reads on it can // complete. send is atomic, and we know the threads completed from the diff --git a/src/utils/mod.rs b/src/utils/mod.rs index c2c5f134dc..ca25f72ff9 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -3,6 +3,7 @@ pub mod notifications; pub mod raw; pub mod toml_utils; pub mod tty; +pub mod units; #[allow(clippy::module_inception)] pub mod utils; diff --git a/src/utils/notifications.rs b/src/utils/notifications.rs index 2881516182..849c6ab828 100644 --- a/src/utils/notifications.rs +++ b/src/utils/notifications.rs @@ -4,6 +4,7 @@ use std::path::Path; use url::Url; use crate::utils::notify::NotificationLevel; +use crate::utils::units::Unit; #[derive(Debug)] pub enum Notification<'a> { @@ -18,12 +19,12 @@ pub enum Notification<'a> { DownloadDataReceived(&'a [u8]), /// Download has finished. DownloadFinished, - /// This thins we're tracking is not counted in bytes. + /// The things we're tracking that are not counted in bytes. /// Must be paired with a pop-units; our other calls are not /// setup to guarantee this any better. - DownloadPushUnits(&'a str), + DownloadPushUnit(Unit), /// finish using an unusual unit. - DownloadPopUnits, + DownloadPopUnit, NoCanonicalPath(&'a Path), ResumingPartialDownload, UsingCurl, @@ -47,8 +48,8 @@ impl<'a> Notification<'a> { | DownloadingFile(_, _) | DownloadContentLengthReceived(_) | DownloadDataReceived(_) - | DownloadPushUnits(_) - | DownloadPopUnits + | DownloadPushUnit(_) + | DownloadPopUnit | DownloadFinished | ResumingPartialDownload | UsingCurl @@ -80,8 +81,8 @@ impl<'a> Display for Notification<'a> { DownloadingFile(url, _) => write!(f, "downloading file from: '{}'", url), DownloadContentLengthReceived(len) => write!(f, "download size is: '{}'", len), DownloadDataReceived(data) => write!(f, "received some data of size {}", data.len()), - DownloadPushUnits(_) => Ok(()), - DownloadPopUnits => Ok(()), + DownloadPushUnit(_) => Ok(()), + DownloadPopUnit => Ok(()), DownloadFinished => write!(f, "download finished"), NoCanonicalPath(path) => write!(f, "could not canonicalize path: '{}'", path.display()), ResumingPartialDownload => write!(f, "resuming partial download"), diff --git a/src/utils/units.rs b/src/utils/units.rs new file mode 100644 index 0000000000..1c94ecce69 --- /dev/null +++ b/src/utils/units.rs @@ -0,0 +1,156 @@ +use std::fmt::{self, Display}; + +#[derive(Copy, Clone, Debug)] +pub enum Unit { + B, + IO, +} + +pub enum UnitMode { + Norm, + Rate, +} + +/// Human readable size (some units) +pub enum Size { + B(usize, UnitMode), + IO(usize, UnitMode), +} + +impl Size { + pub fn new(size: usize, unit: Unit, unitmode: UnitMode) -> Self { + match unit { + Unit::B => Self::B(size, unitmode), + Unit::IO => Self::IO(size, unitmode), + } + } +} + +impl Display for Size { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Size::B(size, unitmode) => { + const KI: f64 = 1024.0; + const MI: f64 = KI * KI; + const GI: f64 = KI * KI * KI; + let size = *size as f64; + + let suffix: String = match unitmode { + UnitMode::Norm => "".into(), + UnitMode::Rate => "/s".into(), + }; + + if size >= GI { + write!(f, "{:5.1} GiB{}", size / GI, suffix) + } else if size >= MI { + write!(f, "{:5.1} MiB{}", size / MI, suffix) + } else if size >= KI { + write!(f, "{:5.1} KiB{}", size / KI, suffix) + } else { + write!(f, "{:3.0} B{}", size, suffix) + } + } + Size::IO(size, unitmode) => { + const K: f64 = 1000.0; + const M: f64 = K * K; + const G: f64 = K * K * K; + let size = *size as f64; + + let suffix: String = match unitmode { + UnitMode::Norm => "IO-ops".into(), + UnitMode::Rate => "IOPS".into(), + }; + + if size >= G { + write!(f, "{:5.1} giga-{}", size / G, suffix) + } else if size >= M { + write!(f, "{:5.1} mega-{}", size / M, suffix) + } else if size >= K { + write!(f, "{:5.1} kilo-{}", size / K, suffix) + } else { + write!(f, "{:3.0} {}", size, suffix) + } + } + } + } +} + +#[cfg(test)] +mod tests { + #[test] + fn unit_formatter_test() { + use crate::utils::units::{Size, Unit, UnitMode}; + + // Test Bytes + assert_eq!( + format!("{}", Size::new(1 as usize, Unit::B, UnitMode::Norm)), + " 1 B" + ); + assert_eq!( + format!("{}", Size::new(1024 as usize, Unit::B, UnitMode::Norm)), + " 1.0 KiB" + ); + assert_eq!( + format!("{}", Size::new(1024usize.pow(2), Unit::B, UnitMode::Norm)), + " 1.0 MiB" + ); + assert_eq!( + format!("{}", Size::new(1024usize.pow(3), Unit::B, UnitMode::Norm)), + " 1.0 GiB" + ); + + // Test Bytes at given rate + assert_eq!( + format!("{}", Size::new(1 as usize, Unit::B, UnitMode::Rate)), + " 1 B/s" + ); + assert_eq!( + format!("{}", Size::new(1024 as usize, Unit::B, UnitMode::Rate)), + " 1.0 KiB/s" + ); + assert_eq!( + format!("{}", Size::new(1024usize.pow(2), Unit::B, UnitMode::Rate)), + " 1.0 MiB/s" + ); + assert_eq!( + format!("{}", Size::new(1024usize.pow(3), Unit::B, UnitMode::Rate)), + " 1.0 GiB/s" + ); + + //Test I/O Operations + assert_eq!( + format!("{}", Size::new(1 as usize, Unit::IO, UnitMode::Norm)), + " 1 IO-ops" + ); + assert_eq!( + format!("{}", Size::new(1000 as usize, Unit::IO, UnitMode::Norm)), + " 1.0 kilo-IO-ops" + ); + assert_eq!( + format!("{}", Size::new(1000usize.pow(2), Unit::IO, UnitMode::Norm)), + " 1.0 mega-IO-ops" + ); + assert_eq!( + format!("{}", Size::new(1000usize.pow(3), Unit::IO, UnitMode::Norm)), + " 1.0 giga-IO-ops" + ); + + //Test I/O Operations at given rate + assert_eq!( + format!("{}", Size::new(1 as usize, Unit::IO, UnitMode::Rate)), + " 1 IOPS" + ); + assert_eq!( + format!("{}", Size::new(1000 as usize, Unit::IO, UnitMode::Rate)), + " 1.0 kilo-IOPS" + ); + assert_eq!( + format!("{}", Size::new(1000usize.pow(2), Unit::IO, UnitMode::Rate)), + " 1.0 mega-IOPS" + ); + assert_eq!( + format!("{}", Size::new(1000usize.pow(3), Unit::IO, UnitMode::Rate)), + " 1.0 giga-IOPS" + ); + } +}