1use super::HumanCountData;
2use crate::utils::{self, SPACE};
3use std::fmt::{self, Debug, Display};
4
5const SPEC: &[&str] = {
7 match (cfg!(feature = "iec"), cfg!(feature = "1024")) {
8 (false, false) => &["", "k", "M", "G", "T", "P", "E", "Z", "Y"], (false, true) => &["", "K", "M", "G", "T", "P", "E", "Z", "Y"], (true, _) => &["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi"], }
12};
13const DECIMALS: &[usize] = &[1, 1, 1, 2, 2, 2, 2, 2, 2];
14const DIVISOR: f64 = {
15 match cfg!(feature = "1024") {
16 true => 1024.,
17 false => 1000.,
18 }
19};
20
21impl Display for HumanCountData<'_> {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 let HumanCountData { mut val, unit } = self;
24 for (&scale, &dec) in SPEC.iter().zip(DECIMALS) {
25 match utils::rounded(val, dec) {
26 r if r.abs() >= DIVISOR => val /= DIVISOR,
27 r if r.fract() == 0. => return write!(f, "{:.0}{}{}{}", r, SPACE, scale, unit),
28 r if (r * 10.).fract() == 0. => {
29 return write!(f, "{:.1}{}{}{}", r, SPACE, scale, unit)
30 }
31 r => return write!(f, "{:.2}{}{}{}", r, SPACE, scale, unit),
32 }
33 }
34
35 write!(f, "{:.2}{}+{}", val, SPACE, unit)
36 }
37}
38
39impl Debug for HumanCountData<'_> {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 let mut ds = f.debug_struct("HumanCount");
42 ds.field("val", &self.val);
43 ds.field("unit", &self.unit);
44 ds.finish()?;
45 write!(f, " -> ")?;
46 fmt::Display::fmt(self, f)
47 }
48}
49
50impl PartialEq<HumanCountData<'_>> for &str {
51 fn eq(&self, other: &HumanCountData<'_>) -> bool {
52 utils::display_compare(self, other)
53 }
54}
55
56impl PartialEq<&str> for HumanCountData<'_> {
57 fn eq(&self, other: &&str) -> bool {
58 other == self
59 }
60}
61
62#[cfg(all(test, not(any(feature = "1024", feature = "iec", feature = "space"))))]
63mod tests {
64 use crate::HumanCount;
65
66 #[test]
67 fn operation() {
68 assert_eq!("123kB", 123000_u64.human_count_bytes());
69 assert_eq!("123.5kB", 123456_u64.human_count_bytes());
70 assert_eq!("23B", 23u8.human_count_bytes());
71 assert_eq!("23B", 23i8.human_count_bytes());
72 assert_eq!("23.5B", 23.5123.human_count_bytes());
73 assert_eq!("-23B", (-23i8).human_count_bytes());
74 assert_eq!("1kB", 1025u16.human_count_bytes());
75 assert_eq!("-1kB", (-1025i16).human_count_bytes());
76 assert_eq!("43.2MB", 43214321u32.human_count_bytes());
77 assert_eq!("23.4GB", 23403454432_u64.human_count_bytes());
78 assert_eq!("23.43GB", 23433454432_u64.human_count_bytes());
79 assert_eq!("18.45EB", u64::MAX.human_count_bytes());
80 assert_eq!("9.22EB", i64::MAX.human_count_bytes());
81 assert_eq!("-9.22EB", i64::MIN.human_count_bytes());
82 assert_eq!("340282366920.94+B", u128::MAX.human_count_bytes());
83 }
84
85 #[test]
86 fn flexibility() {
87 assert_eq!("123MCrabs", 123e6.human_count("Crabs"));
88 assert_eq!("123MCrabs", 123e6.human_count("Crabs".to_owned()));
89 assert_eq!("123MCrabs", 123e6.human_count(&"Crabs".to_owned()));
90 assert_eq!("123k🦀", 123e3.human_count("🦀"));
91 assert_eq!("12.3k°C", 123e2.human_count("°C"));
92 assert_eq!("1.2°C", 123e-2.human_count("°C"));
93 }
94
95 #[test]
96 #[allow(clippy::needless_borrow)]
97 fn ownership() {
98 let mut a = 42000;
99 assert_eq!("42kB", a.human_count_bytes());
100 assert_eq!("42kB", (&a).human_count_bytes());
101 assert_eq!("42kB", (&mut a).human_count_bytes());
102 }
103
104 #[test]
105 fn symmetric() {
106 assert_eq!(123000_u64.human_count_bytes(), "123kB");
107 }
108}
109
110#[test]
111#[cfg(feature = "serde")]
112fn serialize() -> Result<(), serde_json::Error> {
113 use crate::HumanCount;
114 let h = 123456.human_count("X");
115 let ser = serde_json::to_string(&h)?;
116 assert_eq!(r#"{"val":123456.0,"unit":"X"}"#, &ser);
117 let h2 = serde_json::from_str::<HumanCountData>(&ser)?;
118 assert_eq!(h, h2);
119 Ok(())
120}