blob: 592692ff5814798020b48a80012b8c1cabc3b419 [file] [log] [blame]
use std::fmt::{self, Write};
use crate::{
progress::Step,
unit::{DisplayValue, Unit},
};
/// The location at which [`Throughput`] or [`UnitDisplays`][UnitDisplay] should be placed.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
#[allow(missing_docs)]
pub enum Location {
BeforeValue,
AfterUnit,
}
/// A structure able to display throughput, a value change within a given duration.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
pub struct Throughput {
/// The change of value between the current value and the previous one.
pub value_change_in_timespan: Step,
/// The amount of time passed between the previous and the current value.
pub timespan: std::time::Duration,
}
impl Throughput {
/// A convenience method to create a new ThroughPut from `value_change_in_timespan` and `timespan`.
pub fn new(value_change_in_timespan: Step, timespan: std::time::Duration) -> Self {
Throughput {
value_change_in_timespan,
timespan,
}
}
}
/// A way to display a [Unit].
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
pub struct Mode {
location: Location,
percent: bool,
throughput: bool,
}
impl Mode {
fn percent_location(&self) -> Option<Location> {
if self.percent {
Some(self.location)
} else {
None
}
}
fn throughput_location(&self) -> Option<Location> {
if self.throughput {
Some(self.location)
} else {
None
}
}
}
/// initialization and modification
impl Mode {
/// Create a mode instance with percentage only.
pub fn with_percentage() -> Self {
Mode {
percent: true,
throughput: false,
location: Location::AfterUnit,
}
}
/// Create a mode instance with throughput only.
pub fn with_throughput() -> Self {
Mode {
percent: false,
throughput: true,
location: Location::AfterUnit,
}
}
/// Turn on percentage display on the current instance.
pub fn and_percentage(mut self) -> Self {
self.percent = true;
self
}
/// Turn on throughput display on the current instance.
pub fn and_throughput(mut self) -> Self {
self.throughput = true;
self
}
/// Change the display location to show up in front of the value.
pub fn show_before_value(mut self) -> Self {
self.location = Location::BeforeValue;
self
}
}
/// A utility to implement [Display][std::fmt::Display].
pub struct UnitDisplay<'a> {
pub(crate) current_value: Step,
pub(crate) upper_bound: Option<Step>,
pub(crate) throughput: Option<Throughput>,
pub(crate) parent: &'a Unit,
pub(crate) display: What,
}
pub(crate) enum What {
ValuesAndUnit,
Unit,
Values,
}
impl What {
fn values(&self) -> bool {
matches!(self, What::Values | What::ValuesAndUnit)
}
fn unit(&self) -> bool {
matches!(self, What::Unit | What::ValuesAndUnit)
}
}
impl<'a> UnitDisplay<'a> {
/// Display everything, values and the unit.
pub fn all(&mut self) -> &Self {
self.display = What::ValuesAndUnit;
self
}
/// Display only values.
pub fn values(&mut self) -> &Self {
self.display = What::Values;
self
}
/// Display only units.
pub fn unit(&mut self) -> &Self {
self.display = What::Unit;
self
}
}
impl<'a> fmt::Display for UnitDisplay<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let unit: &dyn DisplayValue = self.parent.as_display_value();
let mode = self.parent.mode;
let percent_location_and_fraction = self.upper_bound.and_then(|upper| {
mode.and_then(|m| m.percent_location())
.map(|location| (location, ((self.current_value as f64 / upper as f64) * 100.0).floor()))
});
let throughput_and_location = self.throughput.as_ref().and_then(|throughput| {
mode.and_then(|m| m.throughput_location())
.map(|location| (location, throughput))
});
if self.display.values() {
if let Some((Location::BeforeValue, fraction)) = percent_location_and_fraction {
unit.display_percentage(f, fraction)?;
f.write_char(' ')?;
}
if let Some((Location::BeforeValue, throughput)) = throughput_and_location {
unit.display_throughput(f, throughput)?;
f.write_char(' ')?;
}
unit.display_current_value(f, self.current_value, self.upper_bound)?;
if let Some(upper) = self.upper_bound {
unit.separator(f, self.current_value, self.upper_bound)?;
unit.display_upper_bound(f, upper, self.current_value)?;
}
}
if self.display.unit() {
let mut buf = String::with_capacity(10);
if self.display.values() {
buf.write_char(' ')?;
}
unit.display_unit(&mut buf, self.current_value)?;
if buf.len() > 1 {
// did they actually write a unit?
f.write_str(&buf)?;
}
if let Some((Location::AfterUnit, fraction)) = percent_location_and_fraction {
f.write_char(' ')?;
unit.display_percentage(f, fraction)?;
}
if let Some((Location::AfterUnit, throughput)) = throughput_and_location {
f.write_char(' ')?;
unit.display_throughput(f, throughput)?;
}
}
Ok(())
}
}