#![warn(missing_docs)]
const SUFFIXES: [char; 4] = ['k', 'M', 'B', 'T'];
pub trait PrettyNumber {
fn pretty_format(self) -> String;
}
impl<N: Into<i64>> PrettyNumber for N {
fn pretty_format(self) -> String {
let number: i64 = self.into();
if number.abs() < 1000 {
number.to_string()
} else {
let sign: i8 = if number < 0 { -1 } else { 1 };
let mut number_as_float = number.abs() as f32;
for suffix in SUFFIXES {
number_as_float /= 1000f32;
if number_as_float < 1000f32 {
return format!(
"{:.*}{suffix}",
if (number_as_float - number_as_float.floor()) < 0.05
|| (number_as_float.ceil() - number_as_float) < 0.05
|| number_as_float >= 100f32
{
0
} else {
1
},
sign as f32 * number_as_float
);
}
}
panic!("Number {number} is larger than 1 quadrillion!");
}
}
}
#[cfg(test)]
mod test {
use crate::PrettyNumber;
use rstest::rstest;
#[rstest]
#[case(7, "7")]
#[case(42, "42")]
#[case(717, "717")]
#[case(-5, "-5")]
#[case(-76, "-76")]
#[case(-224, "-224")]
#[case(1_001, "1k")]
#[case(1_624, "1.6k")]
#[case(-5_020, "-5k")]
#[case(-9_505, "-9.5k")]
#[case(19_995, "20k")]
#[case(19_007, "19k")]
#[case(73_444, "73.4k")]
#[case(-55_033, "-55k")]
#[case(-42_780, "-42.8k")]
#[case(469_070, "469k")]
#[case(945_661, "946k")]
#[case(-223_090, "-223k")]
#[case(-671_522, "-672k")]
#[case(3_001_500, "3M")]
#[case(7_926_400, "7.9M")]
#[case(-4_030_115, "-4M")]
#[case(-3_333_221, "-3.3M")]
#[case(75_032_115, "75M")]
#[case(23_333_452, "23.3M")]
#[case(79_998_001, "80M")]
#[case(-54_012_560, "-54M")]
#[case(-23_981_670, "-24M")]
#[case(-11_740_662, "-11.7M")]
#[case(555_067_885, "555M")]
#[case(352_344_120, "352M")]
#[case(-222_000_554, "-222M")]
#[case(-434_875_500, "-435M")]
#[case(2_004_254_578, "2B")]
#[case(7_667_973_223, "7.7B")]
#[case(-4_002_154_900, "-4B")]
#[case(-6_534_664_725, "-6.5B")]
#[case(87_050_671_768, "87.1B")]
#[case(79_059_001, "79.1M")]
#[case(44_444_333_222, "44.4B")]
#[case(-32_010_345_093, "-32B")]
#[case(-65_420_132_543, "-65.4B")]
#[case(899_055_111_032, "899B")]
#[case(723_999_324_999, "724B")]
#[case(-666_000_142_543, "-666B")]
#[case(-400_601_897_231, "-401B")]
#[case(5_000_023_667_158, "5T")]
#[case(1_222_333_444_555, "1.2T")]
#[case(-4_000_354_984_333, "-4T")]
#[case(-6_923_000_178_126, "-6.9T")]
#[case(66_001_789_809_223, "66T")]
#[case(93_723_000_151_300, "93.7T")]
#[case(-50_032_745_113_006, "-50T")]
#[case(-11_444_653_221_094, "-11.4T")]
#[case(343_003_766_091_322, "343T")]
#[case(357_455_634_091_722, "357T")]
#[case(-567_023_400_999_234, "-567T")]
#[case(-871_567_223_222_546, "-872T")]
fn pretty_format_test(#[case] input: i64, #[case] expected: &str) {
assert_eq!(input.pretty_format().as_str(), expected);
}
#[rstest]
#[case(1_000_000_000_000_000)]
#[case(-1_000_000_000_000_000)]
#[should_panic]
fn format_quadrillion_should_panic(#[case] num: i64) {
let _ = num.pretty_format();
}
}