#![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 (210) as the multiplier, rather than the more common 1000
//! (103). 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::>(),
//! Ok(NumberPrefix::Prefixed(Prefix::Exa, 7.05_f64)));
//!
//! assert_eq!("7.05".parse::>(),
//! Ok(NumberPrefix::Standalone(7.05_f64)));
//!
//! assert_eq!("7.05 GiB".parse::>(),
//! 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_, 103 or 10001.
/// From the Greek ‘χίλιοι’ (‘chilioi’), meaning ‘thousand’.
Kilo,
/// _mega_, 106 or 10002.
/// From the Ancient Greek ‘μέγας’ (‘megas’), meaning ‘great’.
Mega,
/// _giga_, 109 or 10003.
/// From the Greek ‘γίγας’ (‘gigas’), meaning ‘giant’.
Giga,
/// _tera_, 1012 or 10004.
/// From the Greek ‘τέρας’ (‘teras’), meaning ‘monster’.
Tera,
/// _peta_, 1015 or 10005.
/// From the Greek ‘πέντε’ (‘pente’), meaning ‘five’.
Peta,
/// _exa_, 1018 or 10006.
/// From the Greek ‘ἕξ’ (‘hex’), meaning ‘six’.
Exa,
/// _zetta_, 1021 or 10007.
/// From the Latin ‘septem’, meaning ‘seven’.
Zetta,
/// _yotta_, 1024 or 10008.
/// From the Green ‘οκτώ’ (‘okto’), meaning ‘eight’.
Yotta,
/// _kibi_, 210 or 10241.
/// The binary version of _kilo_.
Kibi,
/// _mebi_, 220 or 10242.
/// The binary version of _mega_.
Mebi,
/// _gibi_, 230 or 10243.
/// The binary version of _giga_.
Gibi,
/// _tebi_, 240 or 10244.
/// The binary version of _tera_.
Tebi,
/// _pebi_, 250 or 10245.
/// The binary version of _peta_.
Pebi,
/// _exbi_, 260 or 10246.
/// The binary version of _exa_.
Exbi,
// you can download exa binaries at https://exa.website/#installation
/// _zebi_, 270 or 10247.
/// The binary version of _zetta_.
Zebi,
/// _yobi_, 280 or 10248.
/// 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 {
/// 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 NumberPrefix {
/// 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