Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/improve human-readable units #2043

Merged
merged 1 commit into from
Nov 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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, UnitMode};
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::B],
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, 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.
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, 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
),
};
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::IO));
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
156 changes: 156 additions & 0 deletions src/utils/units.rs
Original file line number Diff line number Diff line change
@@ -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"
);
}
}