#![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)]
#![cfg_attr(not(feature = "std"), no_std)]
mod parse;
use core::{
fmt,
ops::{Div, Neg},
};
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum Prefix {
Kilo,
Mega,
Giga,
Tera,
Peta,
Exa,
Zetta,
Yotta,
Kibi,
Mebi,
Gibi,
Tebi,
Pebi,
Exbi,
Zebi,
Yobi,
}
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum NumberPrefix<F> {
Standalone(F),
Prefixed(Prefix, F),
}
impl<F: Amounts> NumberPrefix<F> {
pub fn decimal(amount: F) -> Self {
use self::Prefix::*;
Self::format_number(
amount,
Amounts::NUM_1000,
[Kilo, Mega, Giga, Tera, Peta, Exa, Zetta, Yotta],
)
}
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 {
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)
}
}
}
impl fmt::Display for Prefix {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.symbol())
}
}
impl Prefix {
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",
}
}
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",
}
}
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",
}
}
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",
}
}
}
pub trait Amounts: Copy + Sized + PartialOrd + Div<Output = Self> + Neg<Output = Self> {
const NUM_1000: Self;
const NUM_1024: Self;
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.492_19f32)
)
}
#[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]
fn and_so_on() {
assert_eq!(
NumberPrefix::decimal(1_000_000_000_000_000_000_000_000_000f64),
NumberPrefix::Prefixed(Prefix::Yotta, 1000f64)
)
}
}