diff --git a/rtiow/src/human.rs b/rtiow/src/human.rs new file mode 100644 index 0000000..16e9518 --- /dev/null +++ b/rtiow/src/human.rs @@ -0,0 +1,253 @@ +/// From https://raw.githubusercontent.com/BobGneu/human-format-rs/master/src/lib.rs +#![doc(html_root_url = "https://docs.rs/human_format")] + +//! `human_format` provides facilitates creating a formatted string, converting between numbers that are beyond typical +//! needs for humans into a simpler string that conveys the gist of the meaning of the number. +//! +//! ## Setup +//! +//! Add the library to your dependencies listing +//! +//! ```toml +//! [dependencies] +//! human_format = "0.2" +//! ``` +//! +//! Add the crate reference at your crate root +//! +//! ```rust +//! extern crate human_format; +//! ``` +//! +//! Print some human readable strings +//! +//! ```rust +//! // "1.00 k" +//! let tmpStr = human_format::Formatter::new() +//! .format(1000.0); +//! # assert_eq!(tmpStr, "1.00 k"); +//! +//! // "1.00 M" +//! let tmpStr2 = human_format::Formatter::new() +//! .format(1000000.0); +//! # assert_eq!(tmpStr2, "1.00 M"); +//! +//! // "1.00 B" +//! let tmpStr3 = human_format::Formatter::new() +//! .format(1000000000.0); +//! # assert_eq!(tmpStr3, "1.00 B"); +//! ``` +//! +//! If you are so inspired you can even try playing with units and customizing your `Scales` +//! +//! For more examples you should review the examples on github: [tests/demo.rs](https://github.com/BobGneu/human-format-rs/blob/master/tests/demo.rs) +//! + +#[derive(Debug)] +struct ScaledValue { + value: f32, + suffix: String, +} + +/// Entry point to the lib. Use this to handle your formatting needs. +#[derive(Debug)] +pub struct Formatter { + decimals: usize, + separator: String, + scales: Scales, + forced_units: String, + forced_suffix: String, +} + +/// Provide a customized scaling scheme for your own modeling. +#[derive(Debug)] +pub struct Scales { + base: u32, + suffixes: Vec, +} + +impl Formatter { + /// Initializes a new `Formatter` with default values. + pub fn new() -> Self { + Formatter { + decimals: 2, + separator: " ".to_owned(), + scales: Scales::SI(), + forced_units: "".to_owned(), + forced_suffix: "".to_owned(), + } + } + + /// Sets the decimals value for formatting the string. + pub fn with_decimals(&mut self, decimals: usize) -> &mut Self { + self.decimals = decimals; + + self + } + + /// Sets the separator value for formatting the string. + pub fn with_separator(&mut self, separator: &str) -> &mut Self { + self.separator = separator.to_owned(); + + self + } + + /// Sets the scales value. + pub fn with_scales(&mut self, scales: Scales) -> &mut Self { + self.scales = scales; + + self + } + + /// Sets the units value. + pub fn with_units(&mut self, units: &str) -> &mut Self { + self.forced_units = units.to_owned(); + + self + } + + /// Sets the expected suffix value. + pub fn with_suffix(&mut self, suffix: &str) -> &mut Self { + self.forced_suffix = suffix.to_owned(); + + self + } + + /// Formats the number into a string + pub fn format(&self, value: f64) -> String { + if value < 0.0 { + return format!("-{}", self.format(value * -1.0)); + } + + let scaled_value = self.scales.to_scaled_value(value); + + format!( + "{:.width$}{}{}{}", + scaled_value.value, + self.separator, + scaled_value.suffix, + self.forced_units, + width = self.decimals + ) + } + + /// Parse a string back into a float value. + pub fn parse(&self, value: &str) -> f64 { + let v: Vec<&str> = value.split(&self.separator).collect(); + + let result = v.get(0).unwrap().parse::().unwrap(); + + let mut suffix = v.get(1).unwrap().to_string(); + let new_len = suffix.len() - self.forced_units.len(); + + suffix.truncate(new_len); + + let magnitude_multiplier = self.scales.get_magnitude_multipler(&suffix); + + (result * magnitude_multiplier) + } +} + +impl Scales { + /// Instantiates a new `Scales` with SI keys + pub fn new() -> Self { + Scales::SI() + } + + /// Instantiates a new `Scales` with SI keys + pub fn SI() -> Self { + Scales { + base: 1000, + suffixes: vec![ + "".to_owned(), + "k".to_owned(), + "M".to_owned(), + "B".to_owned(), + "T".to_owned(), + "P".to_owned(), + "E".to_owned(), + "Z".to_owned(), + "Y".to_owned(), + ], + } + } + + /// Instantiates a new `Scales` with Binary keys + pub fn Binary() -> Self { + Scales { + base: 1000, + suffixes: vec![ + "".to_owned(), + "ki".to_owned(), + "Mi".to_owned(), + "Gi".to_owned(), + "Ti".to_owned(), + "Pi".to_owned(), + "Ei".to_owned(), + "Zi".to_owned(), + "Yi".to_owned(), + ], + } + } + + /// Sets the base for the `Scales` + pub fn with_base(&mut self, base: u32) -> &mut Self { + self.base = base; + + self + } + + /// Sets the suffixes listing appropriately + pub fn with_suffixes(&mut self, suffixes: Vec<&str>) -> &mut Self { + self.suffixes = Vec::new(); + + for suffix in suffixes { + // This should be to_owned to be clear about intent. + // https://users.rust-lang.org/t/to-string-vs-to-owned-for-string-literals/1441/6 + self.suffixes.push(suffix.to_owned()); + } + + self + } + + fn get_magnitude_multipler(&self, value: &str) -> f64 { + let ndx = 0; + + for ndx in 0..self.suffixes.len() { + println!("{}", self.suffixes[ndx]); + + if value == self.suffixes[ndx] { + return self.base.pow(ndx as u32) as f64; + } + } + + return 0.0; + } + + fn to_scaled_value(&self, value: f64) -> ScaledValue { + let mut index: usize = 0; + let mut _value: f64 = value; + + loop { + if _value < (self.base as f64) { + break; + } + + _value /= self.base as f64; + index += 1; + } + + println!( + "\t\t{}: {} {} --- {}", + value, + index, + self.base.pow(index as u32), + value / (self.base.pow(index as u32) as f64) + ); + + ScaledValue { + value: (value / self.base.pow((index) as u32) as f64) as f32, + suffix: self.suffixes[index].to_owned(), + } + } +}