Skip to content

Commit d1877f8

Browse files
committed
feat: display type
1 parent 0dcc0ac commit d1877f8

File tree

4 files changed

+261
-106
lines changed

4 files changed

+261
-106
lines changed

examples/ls.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ fn main() -> io::Result<()> {
1515

1616
if md.is_file() {
1717
let file_size = md.len();
18-
let file_size = bytesize::ByteSize::b(file_size);
18+
let file_size = bytesize::ByteSize::b(file_size).display().iec_short();
1919

2020
println!("{file_size}\t{file_name}");
2121
} else {

src/display.rs

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
use core::{fmt, write};
2+
3+
use crate::ByteSize;
4+
5+
/// Format / style to use when displaying a [`ByteSize`].
6+
#[derive(Debug, Clone, Copy)]
7+
pub(crate) enum Format {
8+
Iec,
9+
IecShort,
10+
Si,
11+
SiShort,
12+
}
13+
14+
impl Format {
15+
fn unit(self) -> u64 {
16+
match self {
17+
Format::Iec | Format::IecShort => crate::KIB,
18+
Format::Si | Format::SiShort => crate::KB,
19+
}
20+
}
21+
22+
fn unit_base(self) -> f64 {
23+
match self {
24+
Format::Iec | Format::IecShort => crate::LN_KIB,
25+
Format::Si | Format::SiShort => crate::LN_KB,
26+
}
27+
}
28+
29+
fn unit_prefixes(self) -> &'static [u8] {
30+
match self {
31+
Format::Iec | Format::IecShort => crate::UNITS_IEC.as_bytes(),
32+
Format::Si | Format::SiShort => crate::UNITS_SI.as_bytes(),
33+
}
34+
}
35+
36+
fn unit_separator(self) -> &'static str {
37+
match self {
38+
Format::Iec | Format::Si => " ",
39+
Format::IecShort | Format::SiShort => "",
40+
}
41+
}
42+
43+
fn unit_suffix(self) -> &'static str {
44+
match self {
45+
Format::Iec => "iB",
46+
Format::Si => "B",
47+
Format::IecShort | Format::SiShort => "",
48+
}
49+
}
50+
}
51+
52+
/// Formatting display wrapper for [`ByteSize`].
53+
///
54+
/// Supports various styles, see methods. By default, the [`iec()`](Self::iec()) style is used.
55+
///
56+
/// # Examples
57+
///
58+
/// ```
59+
/// # use bytesize::ByteSize;
60+
/// assert_eq!(
61+
/// "1.0 MiB",
62+
/// ByteSize::mib(1).display().iec().to_string(),
63+
/// );
64+
///
65+
/// assert_eq!(
66+
/// "42.0k",
67+
/// ByteSize::kb(42).display().si_short().to_string(),
68+
/// );
69+
/// ```
70+
#[derive(Debug, Clone)]
71+
pub struct Display {
72+
pub(crate) byte_size: ByteSize,
73+
pub(crate) format: Format,
74+
}
75+
76+
impl Display {
77+
/// Format using IEC (binary) units.
78+
///
79+
/// E.g., `4.2 MiB`.
80+
#[must_use]
81+
#[doc(alias = "binary")]
82+
pub fn iec(mut self) -> Self {
83+
self.format = Format::Iec;
84+
self
85+
}
86+
87+
/// Format using SI (decimal) units.
88+
///
89+
/// E.g., `4.2 MB`.
90+
#[must_use]
91+
#[doc(alias = "binary")]
92+
pub fn iec_short(mut self) -> Self {
93+
self.format = Format::IecShort;
94+
self
95+
}
96+
97+
/// Format using a short style and IEC (binary) units.
98+
///
99+
/// E.g., `4.2M`.
100+
#[must_use]
101+
#[doc(alias = "decimal")]
102+
pub fn si(mut self) -> Self {
103+
self.format = Format::Si;
104+
self
105+
}
106+
107+
/// Format using a short style and SI (decimal) units.
108+
///
109+
/// E.g., `4.2M`.
110+
#[must_use]
111+
#[doc(alias = "decimal")]
112+
pub fn si_short(mut self) -> Self {
113+
self.format = Format::SiShort;
114+
self
115+
}
116+
}
117+
118+
impl fmt::Display for Display {
119+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120+
let bytes = self.byte_size.as_u64();
121+
122+
let unit = self.format.unit();
123+
let unit_base = self.format.unit_base();
124+
125+
let unit_prefixes = self.format.unit_prefixes();
126+
let unit_separator = self.format.unit_separator();
127+
let unit_suffix = self.format.unit_suffix();
128+
129+
if bytes < unit {
130+
write!(f, "{bytes}{unit_separator}B")?;
131+
} else {
132+
let size = bytes as f64;
133+
let exp = match (size.ln() / unit_base) as usize {
134+
0 => 1,
135+
e => e,
136+
};
137+
138+
let unit_prefix = unit_prefixes[exp - 1] as char;
139+
140+
write!(
141+
f,
142+
"{:.1}{unit_separator}{unit_prefix}{unit_suffix}",
143+
(size / unit.pow(exp as u32) as f64),
144+
)?;
145+
}
146+
147+
Ok(())
148+
}
149+
}
150+
151+
#[cfg(test)]
152+
mod tests {
153+
use super::*;
154+
155+
#[test]
156+
fn to_string_iec() {
157+
let display = Display {
158+
byte_size: ByteSize::gib(1),
159+
format: Format::Iec,
160+
};
161+
assert_eq!("1.0 GiB", display.to_string());
162+
163+
let display = Display {
164+
byte_size: ByteSize::gb(1),
165+
format: Format::Iec,
166+
};
167+
assert_eq!("953.7 MiB", display.to_string());
168+
}
169+
170+
#[test]
171+
fn to_string_si() {
172+
let display = Display {
173+
byte_size: ByteSize::gib(1),
174+
format: Format::Si,
175+
};
176+
assert_eq!("1.1 GB", display.to_string());
177+
178+
let display = Display {
179+
byte_size: ByteSize::gb(1),
180+
format: Format::Si,
181+
};
182+
assert_eq!("1.0 GB", display.to_string());
183+
}
184+
185+
#[test]
186+
fn to_string_short() {
187+
let display = Display {
188+
byte_size: ByteSize::gib(1),
189+
format: Format::IecShort,
190+
};
191+
assert_eq!("1.0G", display.to_string());
192+
193+
let display = Display {
194+
byte_size: ByteSize::gb(1),
195+
format: Format::IecShort,
196+
};
197+
assert_eq!("953.7M", display.to_string());
198+
}
199+
200+
#[track_caller]
201+
fn assert_to_string(expected: &str, byte_size: ByteSize, format: Format) {
202+
assert_eq!(expected, Display { byte_size, format }.to_string());
203+
}
204+
205+
#[test]
206+
fn test_to_string_as() {
207+
assert_to_string("215 B", ByteSize::b(215), Format::Iec);
208+
assert_to_string("215 B", ByteSize::b(215), Format::Si);
209+
210+
assert_to_string("1.0 KiB", ByteSize::kib(1), Format::Iec);
211+
assert_to_string("1.0 kB", ByteSize::kib(1), Format::Si);
212+
213+
assert_to_string("293.9 KiB", ByteSize::kb(301), Format::Iec);
214+
assert_to_string("301.0 kB", ByteSize::kb(301), Format::Si);
215+
216+
assert_to_string("1.0 MiB", ByteSize::mib(1), Format::Iec);
217+
assert_to_string("1.0 MB", ByteSize::mib(1), Format::Si);
218+
219+
assert_to_string("1.9 GiB", ByteSize::mib(1907), Format::Iec);
220+
assert_to_string("2.0 GB", ByteSize::mib(1908), Format::Si);
221+
222+
assert_to_string("399.6 MiB", ByteSize::mb(419), Format::Iec);
223+
assert_to_string("419.0 MB", ByteSize::mb(419), Format::Si);
224+
225+
assert_to_string("482.4 GiB", ByteSize::gb(518), Format::Iec);
226+
assert_to_string("518.0 GB", ByteSize::gb(518), Format::Si);
227+
228+
assert_to_string("741.2 TiB", ByteSize::tb(815), Format::Iec);
229+
assert_to_string("815.0 TB", ByteSize::tb(815), Format::Si);
230+
231+
assert_to_string("540.9 PiB", ByteSize::pb(609), Format::Iec);
232+
assert_to_string("609.0 PB", ByteSize::pb(609), Format::Si);
233+
}
234+
}

0 commit comments

Comments
 (0)