1use libm::{fabs, modf};
2
3use crate::{scales, utils::f64_eq, BaseUnit, FormatSizeOptions, Kilo, ToF64, Unsigned};
4
5pub struct ISizeFormatter<T: ToF64, O: AsRef<FormatSizeOptions>> {
6 value: T,
7 options: O,
8}
9
10impl<V: ToF64, O: AsRef<FormatSizeOptions>> ISizeFormatter<V, O> {
11 pub fn new(value: V, options: O) -> Self {
12 ISizeFormatter { value, options }
13 }
14}
15
16impl<T: ToF64, O: AsRef<FormatSizeOptions>> core::fmt::Display for ISizeFormatter<T, O> {
17 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
18 let opts = self.options.as_ref();
19 let divider = opts.kilo.value();
20
21 let mut size: f64 = self.value.to_f64();
22 let mut scale_idx = 0;
23
24 if let Some(val) = opts.fixed_at {
25 while scale_idx != val as usize {
26 size /= divider;
27 scale_idx += 1;
28 }
29 } else {
30 while fabs(size) >= divider {
31 size /= divider;
32 scale_idx += 1;
33 }
34 }
35
36 let mut scale = match (opts.units, opts.long_units, opts.base_unit) {
37 (Kilo::Decimal, false, BaseUnit::Byte) => scales::SCALE_DECIMAL[scale_idx],
38 (Kilo::Decimal, true, BaseUnit::Byte) => scales::SCALE_DECIMAL_LONG[scale_idx],
39 (Kilo::Binary, false, BaseUnit::Byte) => scales::SCALE_BINARY[scale_idx],
40 (Kilo::Binary, true, BaseUnit::Byte) => scales::SCALE_BINARY_LONG[scale_idx],
41 (Kilo::Decimal, false, BaseUnit::Bit) => scales::SCALE_DECIMAL_BIT[scale_idx],
42 (Kilo::Decimal, true, BaseUnit::Bit) => scales::SCALE_DECIMAL_BIT_LONG[scale_idx],
43 (Kilo::Binary, false, BaseUnit::Bit) => scales::SCALE_BINARY_BIT[scale_idx],
44 (Kilo::Binary, true, BaseUnit::Bit) => scales::SCALE_BINARY_BIT_LONG[scale_idx],
45 };
46
47 // Remove "s" from the scale if the size is 1.x
48 let (fpart, ipart) = modf(size);
49 if f64_eq(ipart, 1.0)
50 && (opts.long_units || (opts.base_unit == BaseUnit::Bit && scale_idx == 0))
51 {
52 scale = &scale[0..scale.len() - 1];
53 }
54
55 let places = if f64_eq(fpart, 0.0) {
56 opts.decimal_zeroes
57 } else {
58 opts.decimal_places
59 };
60
61 let space = if opts.space_after_value { " " } else { "" };
62
63 write!(f, "{:.*}{}{}{}", places, size, space, scale, opts.suffix)
64 }
65}
66
67impl<'a, U: ToF64 + Unsigned + Copy, O: AsRef<FormatSizeOptions>> From<&'a SizeFormatter<U, O>>
68 for ISizeFormatter<U, &'a O>
69{
70 fn from(source: &'a SizeFormatter<U, O>) -> Self {
71 ISizeFormatter {
72 value: source.value,
73 options: &source.options,
74 }
75 }
76}
77
78pub struct SizeFormatter<T: ToF64 + Unsigned, O: AsRef<FormatSizeOptions>> {
79 value: T,
80 options: O,
81}
82
83impl<V: ToF64 + Unsigned, O: AsRef<FormatSizeOptions>> SizeFormatter<V, O> {
84 pub fn new(value: V, options: O) -> Self {
85 SizeFormatter { value, options }
86 }
87}
88
89impl<T: ToF64 + Unsigned + Copy, O: AsRef<FormatSizeOptions> + Copy> core::fmt::Display
90 for SizeFormatter<T, O>
91{
92 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
93 write!(f, "{}", ISizeFormatter::from(self))
94 }
95}
96