diff --git a/src/cli/download_tracker.rs b/src/cli/download_tracker.rs index dacc12c43fd..85101005970 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}; 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::Bps], 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); 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); 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); 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 777af7dea53..7d44b3c4cc4 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::IOPS)); 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 c2c5f134dcd..ca25f72ff99 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 28815161827..849c6ab8284 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 00000000000..f0b03cfc265 --- /dev/null +++ b/src/utils/units.rs @@ -0,0 +1,99 @@ +use std::fmt::{self, Display}; + +#[derive(Copy, Clone, Debug)] +pub enum Unit { + Bps, + IOPS, +} + +/// Human readable size (some units) +pub enum Size { + Bps(usize), + IOPS(usize), +} + +impl Size { + pub fn new(size: usize, unit: Unit) -> Self { + match unit { + Unit::Bps => Self::Bps(size), + Unit::IOPS => Self::IOPS(size), + } + } +} + +impl Display for Size { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Size::Bps(size) => { + const KI: f64 = 1024.0; + const MI: f64 = KI * KI; + const GI: f64 = KI * KI * KI; + let size = *size as f64; + + if size >= GI { + write!(f, "{:5.1} GiB/s", size / GI) + } else if size >= MI { + write!(f, "{:5.1} MiB/s", size / MI) + } else if size >= KI { + write!(f, "{:5.1} KiB/s", size / KI) + } else { + write!(f, "{:3.0} B/s", size) + } + } + Size::IOPS(size) => { + const K: f64 = 1000.0; + const M: f64 = K * K; + const G: f64 = K * K * K; + let size = *size as f64; + + if size >= G { + write!(f, "{:5.1} giga-IOPS", size / G) + } else if size >= M { + write!(f, "{:5.1} mega-IOPS", size / M) + } else if size >= K { + write!(f, "{:5.1} kilo-IOPS", size / K) + } else { + write!(f, "{:3.0} IOPS", size) + } + } + } + } +} + +#[cfg(test)] +mod tests { + #[test] + fn unit_formatter_test() { + use crate::utils::units::{Size, Unit}; + + // Test Byte units + assert_eq!(format!("{}", Size::new(1 as usize, Unit::Bps)), " 1 B/s"); + assert_eq!( + format!("{}", Size::new(1024 as usize, Unit::Bps)), + " 1.0 KiB/s" + ); + assert_eq!( + format!("{}", Size::new(1024usize.pow(2), Unit::Bps)), + " 1.0 MiB/s" + ); + assert_eq!( + format!("{}", Size::new(1024usize.pow(3), Unit::Bps)), + " 1.0 GiB/s" + ); + + //Test IOPS + assert_eq!(format!("{}", Size::new(1 as usize, Unit::IOPS)), " 1 IOPS"); + assert_eq!( + format!("{}", Size::new(1000 as usize, Unit::IOPS)), + " 1.0 kilo-IOPS" + ); + assert_eq!( + format!("{}", Size::new(1000usize.pow(2), Unit::IOPS)), + " 1.0 mega-IOPS" + ); + assert_eq!( + format!("{}", Size::new(1000usize.pow(3), Unit::IOPS)), + " 1.0 giga-IOPS" + ); + } +}