1#![no_std]
2use core::fmt::{self, Debug, Display, Formatter, Write};
3mod constants;
4mod convenience;
5pub use constants::*;
6pub use convenience::*;
7
8pub struct Size(BYTES);
9
10impl Display for Size {
11 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
14 self.fmt_with_units(f, UNITS.iter().copied())
15 }
16}
17
18impl Debug for Size {
19 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
21 self.fmt_with_units(f, UNITS_SI.iter().copied())
22 }
23}
24
25impl<'a> Size {
26 fn fmt_with_units<I>(&self, f: &mut Formatter<'_>, mut units: I) -> fmt::Result
27 where
28 I: Iterator<Item = ByteUnit<'a>> + core::iter::DoubleEndedIterator,
29 {
30 let bytes = self.0;
31 let Some(ByteUnit { size, name }) = units.rfind(|unit| bytes >= unit.size) else {
32 return f.write_char('0');
33 };
34 let converted = bytes / size;
35 f.write_fmt(format_args!("{converted}{name}"))
36 }
37}
38
39pub struct LongSize(BYTES);
40
41impl Display for LongSize {
42 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
45 self.fmt_with_units(f, UNITS.iter().copied())
46 }
47}
48
49impl Debug for LongSize {
50 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
52 self.fmt_with_units(f, UNITS_SI.iter().copied())
53 }
54}
55
56impl<'a> LongSize {
57 fn fmt_with_units<I>(&self, f: &mut Formatter<'_>, units: I) -> fmt::Result
58 where
59 I: Iterator<Item = ByteUnit<'a>> + core::iter::DoubleEndedIterator,
60 {
61 let mut bytes = self.0;
62 let mut units = units
63 .rev();
65 let first_unit = 'firstunit: {
67 for unit in &mut units {
68 if bytes >= unit.size {
69 break 'firstunit Some(unit);
70 }
71 }
72 None
73 };
74 let Some(ByteUnit { size, name }) = first_unit else {
75 return f.write_char('0');
76 };
77 let converted = bytes / size;
78 bytes -= converted * size;
79 write!(f, "{converted}{name}")?;
80 for ByteUnit { size, name } in &mut units {
81 if bytes < size {
83 continue;
84 }
85 let converted = bytes / size;
86 bytes -= converted * size;
87 write!(f, " {converted}{name}")?
88 }
89
90 Ok(())
91 }
92}
93
94pub struct DecimalSize(BYTES);
95
96impl Display for DecimalSize {
97 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
100 self.fmt_with_units(f, UNITS.iter().copied())
101 }
102}
103
104impl Debug for DecimalSize {
105 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
107 self.fmt_with_units(f, UNITS_SI.iter().copied())
108 }
109}
110
111impl<'a> DecimalSize {
112 fn fmt_with_units<I>(&self, f: &mut Formatter<'_>, mut units: I) -> fmt::Result
113 where
114 I: Iterator<Item = ByteUnit<'a>> + core::iter::DoubleEndedIterator,
115 {
116 let bytes = self.0;
117 let Some(smallest) = units.next() else {
118 return f.write_char('0');
119 };
120 let ByteUnit { size, name } = units
121 .rfind(|unit| bytes >= unit.size)
123 .unwrap_or(smallest);
124
125 let converted = bytes as f32 / size as f32;
126
127 let round = converted % 1.0 < 0.1;
128 if round {
129 f.write_fmt(format_args!("{converted:.0}{name}"))
130 } else {
131 f.write_fmt(format_args!("{converted:.1}{name}"))
132 }
133 }
134}
135
136#[derive(Clone, Copy, Debug)]
137pub struct ByteUnit<'a> {
138 size: BYTES,
139 name: &'a str,
140}
141
142impl<'a> ByteUnit<'a> {
143 pub const fn new(size: BYTES, name: &'a str) -> Self {
144 Self { size, name }
145 }
146}
147
148mod tests;