Skip to content

Commit 5f599dc

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

File tree

4 files changed

+270
-106
lines changed

4 files changed

+270
-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: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
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+
///
91+
/// Designed to produce output compatible with `sort -h`.
92+
#[must_use]
93+
#[doc(alias = "binary")]
94+
pub fn iec_short(mut self) -> Self {
95+
self.format = Format::IecShort;
96+
self
97+
}
98+
99+
/// Format using a short style and IEC (binary) units.
100+
///
101+
/// E.g., `4.2M`.
102+
#[must_use]
103+
#[doc(alias = "decimal")]
104+
pub fn si(mut self) -> Self {
105+
self.format = Format::Si;
106+
self
107+
}
108+
109+
/// Format using a short style and SI (decimal) units.
110+
///
111+
/// E.g., `4.2M`.
112+
#[must_use]
113+
#[doc(alias = "decimal")]
114+
pub fn si_short(mut self) -> Self {
115+
self.format = Format::SiShort;
116+
self
117+
}
118+
}
119+
120+
impl fmt::Display for Display {
121+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122+
let bytes = self.byte_size.as_u64();
123+
124+
let unit = self.format.unit();
125+
let unit_base = self.format.unit_base();
126+
127+
let unit_prefixes = self.format.unit_prefixes();
128+
let unit_separator = self.format.unit_separator();
129+
let unit_suffix = self.format.unit_suffix();
130+
131+
if bytes < unit {
132+
write!(f, "{bytes}{unit_separator}B")?;
133+
} else {
134+
let size = bytes as f64;
135+
let exp = match (size.ln() / unit_base) as usize {
136+
0 => 1,
137+
e => e,
138+
};
139+
140+
let unit_prefix = unit_prefixes[exp - 1] as char;
141+
142+
write!(
143+
f,
144+
"{:.1}{unit_separator}{unit_prefix}{unit_suffix}",
145+
(size / unit.pow(exp as u32) as f64),
146+
)?;
147+
}
148+
149+
Ok(())
150+
}
151+
}
152+
153+
#[cfg(test)]
154+
mod tests {
155+
use super::*;
156+
157+
#[test]
158+
fn to_string_iec() {
159+
let display = Display {
160+
byte_size: ByteSize::gib(1),
161+
format: Format::Iec,
162+
};
163+
assert_eq!("1.0 GiB", display.to_string());
164+
165+
let display = Display {
166+
byte_size: ByteSize::gb(1),
167+
format: Format::Iec,
168+
};
169+
assert_eq!("953.7 MiB", display.to_string());
170+
}
171+
172+
#[test]
173+
fn to_string_si() {
174+
let display = Display {
175+
byte_size: ByteSize::gib(1),
176+
format: Format::Si,
177+
};
178+
assert_eq!("1.1 GB", display.to_string());
179+
180+
let display = Display {
181+
byte_size: ByteSize::gb(1),
182+
format: Format::Si,
183+
};
184+
assert_eq!("1.0 GB", display.to_string());
185+
}
186+
187+
#[test]
188+
fn to_string_short() {
189+
let display = Display {
190+
byte_size: ByteSize::gib(1),
191+
format: Format::IecShort,
192+
};
193+
assert_eq!("1.0G", display.to_string());
194+
195+
let display = Display {
196+
byte_size: ByteSize::gb(1),
197+
format: Format::IecShort,
198+
};
199+
assert_eq!("953.7M", display.to_string());
200+
}
201+
202+
#[track_caller]
203+
fn assert_to_string(expected: &str, byte_size: ByteSize, format: Format) {
204+
assert_eq!(expected, Display { byte_size, format }.to_string());
205+
}
206+
207+
#[test]
208+
fn test_to_string_as() {
209+
assert_to_string("215 B", ByteSize::b(215), Format::Iec);
210+
assert_to_string("215 B", ByteSize::b(215), Format::Si);
211+
212+
assert_to_string("1.0 KiB", ByteSize::kib(1), Format::Iec);
213+
assert_to_string("1.0 kB", ByteSize::kib(1), Format::Si);
214+
215+
assert_to_string("293.9 KiB", ByteSize::kb(301), Format::Iec);
216+
assert_to_string("301.0 kB", ByteSize::kb(301), Format::Si);
217+
218+
assert_to_string("1.0 MiB", ByteSize::mib(1), Format::Iec);
219+
assert_to_string("1.0 MB", ByteSize::mib(1), Format::Si);
220+
221+
assert_to_string("1.9 GiB", ByteSize::mib(1907), Format::Iec);
222+
assert_to_string("2.0 GB", ByteSize::mib(1908), Format::Si);
223+
224+
assert_to_string("399.6 MiB", ByteSize::mb(419), Format::Iec);
225+
assert_to_string("419.0 MB", ByteSize::mb(419), Format::Si);
226+
227+
assert_to_string("482.4 GiB", ByteSize::gb(518), Format::Iec);
228+
assert_to_string("518.0 GB", ByteSize::gb(518), Format::Si);
229+
230+
assert_to_string("741.2 TiB", ByteSize::tb(815), Format::Iec);
231+
assert_to_string("815.0 TB", ByteSize::tb(815), Format::Si);
232+
233+
assert_to_string("540.9 PiB", ByteSize::pb(609), Format::Iec);
234+
assert_to_string("609.0 PB", ByteSize::pb(609), Format::Si);
235+
}
236+
}

0 commit comments

Comments
 (0)