diff options
Diffstat (limited to 'vendor/number_prefix/src')
-rw-r--r-- | vendor/number_prefix/src/lib.rs | 553 | ||||
-rw-r--r-- | vendor/number_prefix/src/parse.rs | 87 |
2 files changed, 640 insertions, 0 deletions
diff --git a/vendor/number_prefix/src/lib.rs b/vendor/number_prefix/src/lib.rs new file mode 100644 index 000000000..20741e203 --- /dev/null +++ b/vendor/number_prefix/src/lib.rs @@ -0,0 +1,553 @@ +#![deny(unsafe_code)] +#![warn(missing_copy_implementations)] +#![warn(missing_debug_implementations)] +#![warn(missing_docs)] +#![warn(nonstandard_style)] +#![warn(trivial_numeric_casts)] +#![warn(unreachable_pub)] +#![warn(unused)] + + +//! This is a library for formatting numbers with numeric prefixes, such as +//! turning “3000 metres” into “3 kilometres”, or “8705 bytes” into “8.5 KiB”. +//! +//! +//! # Usage +//! +//! The function [`NumberPrefix::decimal`](enum.NumberPrefix.html#method.decimal) +//! returns either a pair of the resulting number and its prefix, or a +//! notice that the number was too small to have any prefix applied to it. For +//! example: +//! +//! ``` +//! use number_prefix::NumberPrefix; +//! +//! let amount = 8542_f32; +//! let result = match NumberPrefix::decimal(amount) { +//! NumberPrefix::Standalone(bytes) => { +//! format!("The file is {} bytes in size", bytes) +//! } +//! NumberPrefix::Prefixed(prefix, n) => { +//! format!("The file is {:.1} {}B in size", n, prefix) +//! } +//! }; +//! +//! assert_eq!("The file is 8.5 kB in size", result); +//! ``` +//! +//! The `{:.1}` part of the formatting string tells it to restrict the +//! output to only one decimal place. This value is calculated by repeatedly +//! dividing the number by 1000 until it becomes less than that, which in this +//! case results in 8.542, which gets rounded down. Because only one division +//! had to take place, the function also returns the decimal prefix `Kilo`, +//! which gets converted to its internationally-recognised symbol when +//! formatted as a string. +//! +//! If the value is too small to have any prefixes applied to it — in this case, +//! if it’s under 1000 — then the standalone value will be returned: +//! +//! ``` +//! use number_prefix::NumberPrefix; +//! +//! let amount = 705_f32; +//! let result = match NumberPrefix::decimal(amount) { +//! NumberPrefix::Standalone(bytes) => { +//! format!("The file is {} bytes in size", bytes) +//! } +//! NumberPrefix::Prefixed(prefix, n) => { +//! format!("The file is {:.1} {}B in size", n, prefix) +//! } +//! }; +//! +//! assert_eq!("The file is 705 bytes in size", result); +//! ``` +//! +//! In this particular example, the user expects different formatting for +//! both bytes and kilobytes: while prefixed values are given more precision, +//! there’s no point using anything other than whole numbers for just byte +//! amounts. This is why the function pays attention to values without any +//! prefixes — they often need to be special-cased. +//! +//! +//! ## Binary Prefixes +//! +//! This library also allows you to use the *binary prefixes*, which use the +//! number 1024 (2<sup>10</sup>) as the multiplier, rather than the more common 1000 +//! (10<sup>3</sup>). This uses the +//! [`NumberPrefix::binary`](enum.NumberPrefix.html#method.binary) function. +//! For example: +//! +//! ``` +//! use number_prefix::NumberPrefix; +//! +//! let amount = 8542_f32; +//! let result = match NumberPrefix::binary(amount) { +//! NumberPrefix::Standalone(bytes) => { +//! format!("The file is {} bytes in size", bytes) +//! } +//! NumberPrefix::Prefixed(prefix, n) => { +//! format!("The file is {:.1} {}B in size", n, prefix) +//! } +//! }; +//! +//! assert_eq!("The file is 8.3 KiB in size", result); +//! ``` +//! +//! A kibibyte is slightly larger than a kilobyte, so the number is smaller +//! in the result; but other than that, it works in exactly the same way, with +//! the binary prefix being converted to a symbol automatically. +//! +//! +//! ## Which type of prefix should I use? +//! +//! There is no correct answer this question! Common practice is to use +//! the binary prefixes for numbers of *bytes*, while still using the decimal +//! prefixes for everything else. Computers work with powers of two, rather than +//! powers of ten, and by using the binary prefixes, you get a more accurate +//! representation of the amount of data. +//! +//! +//! ## Prefix Names +//! +//! If you need to describe your unit in actual words, rather than just with the +//! symbol, use one of the `upper`, `caps`, `lower`, or `symbol`, which output the +//! prefix in a variety of formats. For example: +//! +//! ``` +//! use number_prefix::NumberPrefix; +//! +//! let amount = 8542_f32; +//! let result = match NumberPrefix::decimal(amount) { +//! NumberPrefix::Standalone(bytes) => { +//! format!("The file is {} bytes in size", bytes) +//! } +//! NumberPrefix::Prefixed(prefix, n) => { +//! format!("The file is {:.1} {}bytes in size", n, prefix.lower()) +//! } +//! }; +//! +//! assert_eq!("The file is 8.5 kilobytes in size", result); +//! ``` +//! +//! +//! ## String Parsing +//! +//! There is a `FromStr` implementation for `NumberPrefix` that parses +//! strings containing numbers and trailing prefixes, such as `7.5E`. +//! +//! Currently, the only supported units are `b` and `B` for bytes, and `m` for +//! metres. Whitespace is allowed between the number and the rest of the string. +//! +//! ``` +//! use number_prefix::{NumberPrefix, Prefix}; +//! +//! assert_eq!("7.05E".parse::<NumberPrefix<_>>(), +//! Ok(NumberPrefix::Prefixed(Prefix::Exa, 7.05_f64))); +//! +//! assert_eq!("7.05".parse::<NumberPrefix<_>>(), +//! Ok(NumberPrefix::Standalone(7.05_f64))); +//! +//! assert_eq!("7.05 GiB".parse::<NumberPrefix<_>>(), +//! Ok(NumberPrefix::Prefixed(Prefix::Gibi, 7.05_f64))); +//! ``` + + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "std")] +mod parse; + +#[cfg(not(feature = "std"))] +use core::ops::{Neg, Div}; + +#[cfg(feature = "std")] +use std::{fmt, ops::{Neg, Div}}; + + +/// A numeric prefix, either binary or decimal. +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub enum Prefix { + + /// _kilo_, 10<sup>3</sup> or 1000<sup>1</sup>. + /// From the Greek ‘χίλιοι’ (‘chilioi’), meaning ‘thousand’. + Kilo, + + /// _mega_, 10<sup>6</sup> or 1000<sup>2</sup>. + /// From the Ancient Greek ‘μέγας’ (‘megas’), meaning ‘great’. + Mega, + + /// _giga_, 10<sup>9</sup> or 1000<sup>3</sup>. + /// From the Greek ‘γίγας’ (‘gigas’), meaning ‘giant’. + Giga, + + /// _tera_, 10<sup>12</sup> or 1000<sup>4</sup>. + /// From the Greek ‘τέρας’ (‘teras’), meaning ‘monster’. + Tera, + + /// _peta_, 10<sup>15</sup> or 1000<sup>5</sup>. + /// From the Greek ‘πέντε’ (‘pente’), meaning ‘five’. + Peta, + + /// _exa_, 10<sup>18</sup> or 1000<sup>6</sup>. + /// From the Greek ‘ἕξ’ (‘hex’), meaning ‘six’. + Exa, + + /// _zetta_, 10<sup>21</sup> or 1000<sup>7</sup>. + /// From the Latin ‘septem’, meaning ‘seven’. + Zetta, + + /// _yotta_, 10<sup>24</sup> or 1000<sup>8</sup>. + /// From the Green ‘οκτώ’ (‘okto’), meaning ‘eight’. + Yotta, + + /// _kibi_, 2<sup>10</sup> or 1024<sup>1</sup>. + /// The binary version of _kilo_. + Kibi, + + /// _mebi_, 2<sup>20</sup> or 1024<sup>2</sup>. + /// The binary version of _mega_. + Mebi, + + /// _gibi_, 2<sup>30</sup> or 1024<sup>3</sup>. + /// The binary version of _giga_. + Gibi, + + /// _tebi_, 2<sup>40</sup> or 1024<sup>4</sup>. + /// The binary version of _tera_. + Tebi, + + /// _pebi_, 2<sup>50</sup> or 1024<sup>5</sup>. + /// The binary version of _peta_. + Pebi, + + /// _exbi_, 2<sup>60</sup> or 1024<sup>6</sup>. + /// The binary version of _exa_. + Exbi, + // you can download exa binaries at https://exa.website/#installation + + /// _zebi_, 2<sup>70</sup> or 1024<sup>7</sup>. + /// The binary version of _zetta_. + Zebi, + + /// _yobi_, 2<sup>80</sup> or 1024<sup>8</sup>. + /// The binary version of _yotta_. + Yobi, +} + + +/// The result of trying to apply a prefix to a floating-point value. +#[derive(PartialEq, Eq, Clone, Debug)] +pub enum NumberPrefix<F> { + + /// A **standalone** value is returned when the number is too small to + /// have any prefixes applied to it. This is commonly a special case, so + /// is handled separately. + Standalone(F), + + /// A **prefixed** value *is* large enough for prefixes. This holds the + /// prefix, as well as the resulting value. + Prefixed(Prefix, F), +} + +impl<F: Amounts> NumberPrefix<F> { + + /// Formats the given floating-point number using **decimal** prefixes. + /// + /// This function accepts both `f32` and `f64` values. If you’re trying to + /// format an integer, you’ll have to cast it first. + /// + /// # Examples + /// + /// ``` + /// use number_prefix::{Prefix, NumberPrefix}; + /// + /// assert_eq!(NumberPrefix::decimal(1_000_000_000_f32), + /// NumberPrefix::Prefixed(Prefix::Giga, 1_f32)); + /// ``` + pub fn decimal(amount: F) -> Self { + use self::Prefix::*; + Self::format_number(amount, Amounts::NUM_1000, [Kilo, Mega, Giga, Tera, Peta, Exa, Zetta, Yotta]) + } + + /// Formats the given floating-point number using **binary** prefixes. + /// + /// This function accepts both `f32` and `f64` values. If you’re trying to + /// format an integer, you’ll have to cast it first. + /// + /// # Examples + /// + /// ``` + /// use number_prefix::{Prefix, NumberPrefix}; + /// + /// assert_eq!(NumberPrefix::binary(1_073_741_824_f64), + /// NumberPrefix::Prefixed(Prefix::Gibi, 1_f64)); + /// ``` + pub fn binary(amount: F) -> Self { + use self::Prefix::*; + Self::format_number(amount, Amounts::NUM_1024, [Kibi, Mebi, Gibi, Tebi, Pebi, Exbi, Zebi, Yobi]) + } + + fn format_number(mut amount: F, kilo: F, prefixes: [Prefix; 8]) -> Self { + + // For negative numbers, flip it to positive, do the processing, then + // flip it back to negative again afterwards. + let was_negative = if amount.is_negative() { amount = -amount; true } else { false }; + + let mut prefix = 0; + while amount >= kilo && prefix < 8 { + amount = amount / kilo; + prefix += 1; + } + + if was_negative { + amount = -amount; + } + + if prefix == 0 { + NumberPrefix::Standalone(amount) + } + else { + NumberPrefix::Prefixed(prefixes[prefix - 1], amount) + } + } +} + +#[cfg(feature = "std")] +impl fmt::Display for Prefix { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.symbol()) + } +} + +impl Prefix { + + /// Returns the name in uppercase, such as “KILO”. + /// + /// # Examples + /// + /// ``` + /// use number_prefix::Prefix; + /// + /// assert_eq!("GIGA", Prefix::Giga.upper()); + /// assert_eq!("GIBI", Prefix::Gibi.upper()); + /// ``` + pub fn upper(self) -> &'static str { + use self::Prefix::*; + match self { + Kilo => "KILO", Mega => "MEGA", Giga => "GIGA", Tera => "TERA", + Peta => "PETA", Exa => "EXA", Zetta => "ZETTA", Yotta => "YOTTA", + Kibi => "KIBI", Mebi => "MEBI", Gibi => "GIBI", Tebi => "TEBI", + Pebi => "PEBI", Exbi => "EXBI", Zebi => "ZEBI", Yobi => "YOBI", + } + } + + /// Returns the name with the first letter capitalised, such as “Mega”. + /// + /// # Examples + /// + /// ``` + /// use number_prefix::Prefix; + /// + /// assert_eq!("Giga", Prefix::Giga.caps()); + /// assert_eq!("Gibi", Prefix::Gibi.caps()); + /// ``` + pub fn caps(self) -> &'static str { + use self::Prefix::*; + match self { + Kilo => "Kilo", Mega => "Mega", Giga => "Giga", Tera => "Tera", + Peta => "Peta", Exa => "Exa", Zetta => "Zetta", Yotta => "Yotta", + Kibi => "Kibi", Mebi => "Mebi", Gibi => "Gibi", Tebi => "Tebi", + Pebi => "Pebi", Exbi => "Exbi", Zebi => "Zebi", Yobi => "Yobi", + } + } + + /// Returns the name in lowercase, such as “giga”. + /// + /// # Examples + /// + /// ``` + /// use number_prefix::Prefix; + /// + /// assert_eq!("giga", Prefix::Giga.lower()); + /// assert_eq!("gibi", Prefix::Gibi.lower()); + /// ``` + pub fn lower(self) -> &'static str { + use self::Prefix::*; + match self { + Kilo => "kilo", Mega => "mega", Giga => "giga", Tera => "tera", + Peta => "peta", Exa => "exa", Zetta => "zetta", Yotta => "yotta", + Kibi => "kibi", Mebi => "mebi", Gibi => "gibi", Tebi => "tebi", + Pebi => "pebi", Exbi => "exbi", Zebi => "zebi", Yobi => "yobi", + } + } + + /// Returns the short-hand symbol, such as “T” (for “tera”). + /// + /// # Examples + /// + /// ``` + /// use number_prefix::Prefix; + /// + /// assert_eq!("G", Prefix::Giga.symbol()); + /// assert_eq!("Gi", Prefix::Gibi.symbol()); + /// ``` + pub fn symbol(self) -> &'static str { + use self::Prefix::*; + match self { + Kilo => "k", Mega => "M", Giga => "G", Tera => "T", + Peta => "P", Exa => "E", Zetta => "Z", Yotta => "Y", + Kibi => "Ki", Mebi => "Mi", Gibi => "Gi", Tebi => "Ti", + Pebi => "Pi", Exbi => "Ei", Zebi => "Zi", Yobi => "Yi", + } + } +} + +/// Traits for floating-point values for both the possible multipliers. They +/// need to be Copy, have defined 1000 and 1024s, and implement a bunch of +/// operators. +pub trait Amounts: Copy + Sized + PartialOrd + Div<Output=Self> + Neg<Output=Self> { + + /// The constant representing 1000, for decimal prefixes. + const NUM_1000: Self; + + /// The constant representing 1024, for binary prefixes. + const NUM_1024: Self; + + /// Whether this number is negative. + /// This is used internally. + fn is_negative(self) -> bool; +} + +impl Amounts for f32 { + const NUM_1000: Self = 1000_f32; + const NUM_1024: Self = 1024_f32; + + fn is_negative(self) -> bool { + self.is_sign_negative() + } +} + +impl Amounts for f64 { + const NUM_1000: Self = 1000_f64; + const NUM_1024: Self = 1024_f64; + + fn is_negative(self) -> bool { + self.is_sign_negative() + } +} + + +#[cfg(test)] +mod test { + use super::{NumberPrefix, Prefix}; + + #[test] + fn decimal_minus_one_billion() { + assert_eq!(NumberPrefix::decimal(-1_000_000_000_f64), + NumberPrefix::Prefixed(Prefix::Giga, -1f64)) + } + + #[test] + fn decimal_minus_one() { + assert_eq!(NumberPrefix::decimal(-1f64), + NumberPrefix::Standalone(-1f64)) + } + + #[test] + fn decimal_0() { + assert_eq!(NumberPrefix::decimal(0f64), + NumberPrefix::Standalone(0f64)) + } + + #[test] + fn decimal_999() { + assert_eq!(NumberPrefix::decimal(999f32), + NumberPrefix::Standalone(999f32)) + } + + #[test] + fn decimal_1000() { + assert_eq!(NumberPrefix::decimal(1000f32), + NumberPrefix::Prefixed(Prefix::Kilo, 1f32)) + } + + #[test] + fn decimal_1030() { + assert_eq!(NumberPrefix::decimal(1030f32), + NumberPrefix::Prefixed(Prefix::Kilo, 1.03f32)) + } + + #[test] + fn decimal_1100() { + assert_eq!(NumberPrefix::decimal(1100f64), + NumberPrefix::Prefixed(Prefix::Kilo, 1.1f64)) + } + + #[test] + fn decimal_1111() { + assert_eq!(NumberPrefix::decimal(1111f64), + NumberPrefix::Prefixed(Prefix::Kilo, 1.111f64)) + } + + #[test] + fn binary_126456() { + assert_eq!(NumberPrefix::binary(126_456f32), + NumberPrefix::Prefixed(Prefix::Kibi, 123.492188f32)) + } + + #[test] + fn binary_1048576() { + assert_eq!(NumberPrefix::binary(1_048_576f64), + NumberPrefix::Prefixed(Prefix::Mebi, 1f64)) + } + + #[test] + fn binary_1073741824() { + assert_eq!(NumberPrefix::binary(2_147_483_648f32), + NumberPrefix::Prefixed(Prefix::Gibi, 2f32)) + } + + #[test] + fn giga() { + assert_eq!(NumberPrefix::decimal(1_000_000_000f64), + NumberPrefix::Prefixed(Prefix::Giga, 1f64)) + } + + #[test] + fn tera() { + assert_eq!(NumberPrefix::decimal(1_000_000_000_000f64), + NumberPrefix::Prefixed(Prefix::Tera, 1f64)) + } + + #[test] + fn peta() { + assert_eq!(NumberPrefix::decimal(1_000_000_000_000_000f64), + NumberPrefix::Prefixed(Prefix::Peta, 1f64)) + } + + #[test] + fn exa() { + assert_eq!(NumberPrefix::decimal(1_000_000_000_000_000_000f64), + NumberPrefix::Prefixed(Prefix::Exa, 1f64)) + } + + #[test] + fn zetta() { + assert_eq!(NumberPrefix::decimal(1_000_000_000_000_000_000_000f64), + NumberPrefix::Prefixed(Prefix::Zetta, 1f64)) + } + + #[test] + fn yotta() { + assert_eq!(NumberPrefix::decimal(1_000_000_000_000_000_000_000_000f64), + NumberPrefix::Prefixed(Prefix::Yotta, 1f64)) + } + + #[test] + #[allow(overflowing_literals)] + fn and_so_on() { + // When you hit yotta, don't keep going + assert_eq!(NumberPrefix::decimal(1_000_000_000_000_000_000_000_000_000f64), + NumberPrefix::Prefixed(Prefix::Yotta, 1000f64)) + } +} diff --git a/vendor/number_prefix/src/parse.rs b/vendor/number_prefix/src/parse.rs new file mode 100644 index 000000000..cfb37af4a --- /dev/null +++ b/vendor/number_prefix/src/parse.rs @@ -0,0 +1,87 @@ +use std::{error::Error, fmt, str}; + +use super::{NumberPrefix, Prefix}; + + +impl<T: str::FromStr> str::FromStr for NumberPrefix<T> { + type Err = NumberPrefixParseError; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + let splitted = s.find(|p| { + p == 'k' || p == 'K' || p == 'M' || p == 'G' || p == 'T' || + p == 'P' || p == 'E' || p == 'Z' || p == 'Y' + }); + + let num_prefix = s.split_at(splitted.unwrap_or(s.len())); + let num = match num_prefix.0.trim().parse::<T>() { + Ok(n) => n, + Err(_) => return Err(NumberPrefixParseError(())), + }; + + let prefix_unit = num_prefix.1.trim_matches(|p| + p == 'b' || p == 'B' || p == 'm' + ); + + let prefix = match prefix_unit { + "k" | + "K" => Prefix::Kilo, + "M" => Prefix::Mega, + "G" => Prefix::Giga, + "T" => Prefix::Tera, + "P" => Prefix::Peta, + "E" => Prefix::Exa, + "Z" => Prefix::Zetta, + "Y" => Prefix::Yotta, + "Ki" => Prefix::Kibi, + "Mi" => Prefix::Mebi, + "Gi" => Prefix::Gibi, + "Ti" => Prefix::Tebi, + "Pi" => Prefix::Pebi, + "Ei" => Prefix::Exbi, + "Zi" => Prefix::Zebi, + "Yi" => Prefix::Yobi, + "" => return Ok(NumberPrefix::Standalone(num)), + _ => return Err(NumberPrefixParseError(())), + }; + + Ok(NumberPrefix::Prefixed(prefix, num)) + } +} + + +/// The error returned when a `NumberPrefix` is failed to be parsed. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct NumberPrefixParseError(()); + +impl fmt::Display for NumberPrefixParseError { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.write_str("invalid prefix syntax") + } +} + +impl Error for NumberPrefixParseError { +} + + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn parse_examples() { + let parse_example_a = "7.05E".parse::<NumberPrefix<f64>>(); + let parse_example_b = "7.05".parse::<NumberPrefix<f64>>(); + let parse_example_c = "7.05 GiB".parse::<NumberPrefix<f64>>(); + + assert_eq!(parse_example_a, Ok(NumberPrefix::Prefixed(Prefix::Exa, 7.05_f64))); + assert_eq!(parse_example_b, Ok(NumberPrefix::Standalone(7.05_f64))); + assert_eq!(parse_example_c, Ok(NumberPrefix::Prefixed(Prefix::Gibi, 7.05_f64))); + } + + #[test] + fn bad_parse() { + let parsed = "bogo meters per second".parse::<NumberPrefix<f64>>(); + + assert_ne!(parsed, Ok(NumberPrefix::Prefixed(Prefix::Kilo, 7.05_f64))); + } +} |