#![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)]
#[cfg(feature = "std")]
mod parse;
#[cfg(not(feature = "std"))]
use core::ops::{Neg, Div};
#[cfg(feature = "std")]
use std::{fmt, ops::{Neg, Div}};
#[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)
}
}
}
#[cfg(feature = "std")]
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.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() {
assert_eq!(NumberPrefix::decimal(1_000_000_000_000_000_000_000_000_000f64),
NumberPrefix::Prefixed(Prefix::Yotta, 1000f64))
}
}