//! This module contains common units for time and information quantities. use std::marker::PhantomData; use crate::quantity::{ byte, gibibyte, hour, kibibyte, mebibyte, microsecond, millisecond, minute, second, tebibyte, Information, Time, }; pub trait IsUnit { type Quantity; fn dispatch(&self) -> Box>; fn preferred_precision(&self) -> usize; fn short_name(&self) -> &'static str { self.dispatch().short_name() } fn format(&self, value: Self::Quantity, precision: usize) -> String { self.dispatch().format_value(value, precision) } } /// Supported time units #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum TimeUnit { MicroSecond, MilliSecond, Second, Minute, Hour, } impl IsUnit for TimeUnit { type Quantity = Time; fn dispatch(&self) -> Box> { match self { TimeUnit::MicroSecond => Box::new(TimeUnitDispatcher::::new()), TimeUnit::MilliSecond => Box::new(TimeUnitDispatcher::::new()), TimeUnit::Second => Box::new(TimeUnitDispatcher::::new()), TimeUnit::Minute => Box::new(TimeUnitDispatcher::::new()), TimeUnit::Hour => Box::new(TimeUnitDispatcher::::new()), } } fn preferred_precision(&self) -> usize { match self { TimeUnit::Second => 3, _ => 1, } } } /// Supported information units #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum InformationUnit { Byte, KibiByte, MebiByte, GibiByte, TebiByte, } impl IsUnit for InformationUnit { type Quantity = Information; fn dispatch(&self) -> Box> { match self { InformationUnit::Byte => Box::new(InformationUnitDispatcher::::new()), InformationUnit::KibiByte => Box::new(InformationUnitDispatcher::::new()), InformationUnit::MebiByte => Box::new(InformationUnitDispatcher::::new()), InformationUnit::GibiByte => Box::new(InformationUnitDispatcher::::new()), InformationUnit::TebiByte => Box::new(InformationUnitDispatcher::::new()), } } fn preferred_precision(&self) -> usize { match self { InformationUnit::Byte => 0, _ => 1, } } } pub trait UnitImpl { type Quantity; fn short_name(&self) -> &'static str; fn format_value(&self, value: Self::Quantity, precision: usize) -> String; } struct TimeUnitDispatcher> { u: PhantomData, } impl> TimeUnitDispatcher { fn new() -> Self { TimeUnitDispatcher { u: PhantomData } } } impl> UnitImpl for TimeUnitDispatcher { type Quantity = Time; fn short_name(&self) -> &'static str { U::abbreviation() } fn format_value(&self, value: Time, precision: usize) -> String { format!("{value:.precision$}", value = value.get::()) } } struct InformationUnitDispatcher> { u: PhantomData, } impl> InformationUnitDispatcher { fn new() -> Self { InformationUnitDispatcher { u: PhantomData } } } impl> UnitImpl for InformationUnitDispatcher { type Quantity = Information; fn short_name(&self) -> &'static str { U::abbreviation() } fn format_value(&self, value: Information, precision: usize) -> String { format!("{value:.precision$}", value = value.get::()) } } #[test] fn test_time_unit_short_name() { assert_eq!("s", TimeUnit::Second.short_name()); assert_eq!("ms", TimeUnit::MilliSecond.short_name()); assert_eq!("µs", TimeUnit::MicroSecond.short_name()); assert_eq!("min", TimeUnit::Minute.short_name()); assert_eq!("h", TimeUnit::Hour.short_name()); } // Note - the values are rounded when formatted. #[test] fn test_unit_format() { use crate::quantity::FormatQuantity; let value = Time::new::(123.456789); assert_eq!("123.457", value.format_value(TimeUnit::Second)); assert_eq!("123456.8", value.format_value(TimeUnit::MilliSecond)); assert_eq!( "1234.6", Time::new::(0.00123456).format_value(TimeUnit::MicroSecond) ); assert_eq!("2.1", value.format_value(TimeUnit::Minute)); assert_eq!("0.0", value.format_value(TimeUnit::Hour)); }