Skip to content

Commit

Permalink
Fix/improve human-readable units
Browse files Browse the repository at this point in the history
  • Loading branch information
Rudi3 committed Oct 7, 2019
1 parent 97bb7b9 commit 8406bed
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 43 deletions.
50 changes: 16 additions & 34 deletions src/cli/download_tracker.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -43,7 +44,7 @@ pub struct DownloadTracker {
/// rendered, so we can erase it cleanly.
displayed_charcount: Option<usize>,
/// What units to show progress in
units: Vec<String>,
units: Vec<Unit>,
/// Whether we display progress
display_progress: bool,
}
Expand All @@ -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,
}
}
Expand All @@ -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
}

Expand Down Expand Up @@ -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.
Expand All @@ -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
),
};
Expand All @@ -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();
}
}
Expand All @@ -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]
Expand Down
5 changes: 3 additions & 2 deletions src/diskio/threaded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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,
));
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
15 changes: 8 additions & 7 deletions src/utils/notifications.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand All @@ -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,
Expand All @@ -47,8 +48,8 @@ impl<'a> Notification<'a> {
| DownloadingFile(_, _)
| DownloadContentLengthReceived(_)
| DownloadDataReceived(_)
| DownloadPushUnits(_)
| DownloadPopUnits
| DownloadPushUnit(_)
| DownloadPopUnit
| DownloadFinished
| ResumingPartialDownload
| UsingCurl
Expand Down Expand Up @@ -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"),
Expand Down
99 changes: 99 additions & 0 deletions src/utils/units.rs
Original file line number Diff line number Diff line change
@@ -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"
);
}
}

0 comments on commit 8406bed

Please sign in to comment.