Skip to main content

fxcp_core/
fmt.rs

1// SPDX-License-Identifier: GPL-2.0-or-later
2
3//! Shared formatting utilities for human-readable output.
4
5/// Format a byte count using SI-style decimal suffixes (KB, MB, GB).
6///
7/// Uses 1024-based thresholds with decimal suffixes for display consistency
8/// with common filesystem tools (ls, du, df).
9#[must_use]
10pub fn format_size(bytes: u64) -> String {
11    if bytes >= 1024 * 1024 * 1024 {
12        format!("{:.1} GB", bytes as f64 / (1024.0 * 1024.0 * 1024.0))
13    } else if bytes >= 1024 * 1024 {
14        format!("{:.1} MB", bytes as f64 / (1024.0 * 1024.0))
15    } else if bytes >= 1024 {
16        format!("{:.1} KB", bytes as f64 / 1024.0)
17    } else {
18        format!("{bytes} B")
19    }
20}
21
22/// Format a byte count using IEC binary suffixes (KiB, MiB, GiB).
23#[must_use]
24pub fn format_size_iec(bytes: u64) -> String {
25    if bytes >= 1024 * 1024 * 1024 {
26        format!("{:.2} GiB", bytes as f64 / (1024.0 * 1024.0 * 1024.0))
27    } else if bytes >= 1024 * 1024 {
28        format!("{:.2} MiB", bytes as f64 / (1024.0 * 1024.0))
29    } else if bytes >= 1024 {
30        format!("{:.2} KiB", bytes as f64 / 1024.0)
31    } else {
32        format!("{bytes} B")
33    }
34}
35
36/// Format a byte count using compact suffixes without spaces (G, M, K, B).
37///
38/// For space-constrained displays like TUI status bars.
39#[must_use]
40pub fn format_size_compact(bytes: u64) -> String {
41    if bytes >= 1024 * 1024 * 1024 {
42        format!("{:.1}G", bytes as f64 / (1024.0 * 1024.0 * 1024.0))
43    } else if bytes >= 1024 * 1024 {
44        format!("{:.1}M", bytes as f64 / (1024.0 * 1024.0))
45    } else if bytes >= 1024 {
46        format!("{:.0}K", bytes as f64 / 1024.0)
47    } else {
48        format!("{bytes}B")
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    #![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
55    use super::*;
56
57    #[test]
58    fn test_format_size_bytes() {
59        assert_eq!(format_size(0), "0 B");
60        assert_eq!(format_size(512), "512 B");
61        assert_eq!(format_size(1023), "1023 B");
62    }
63
64    #[test]
65    fn test_format_size_kb() {
66        assert_eq!(format_size(1024), "1.0 KB");
67        assert_eq!(format_size(1536), "1.5 KB");
68    }
69
70    #[test]
71    fn test_format_size_mb() {
72        assert_eq!(format_size(1024 * 1024), "1.0 MB");
73        assert_eq!(format_size(10 * 1024 * 1024), "10.0 MB");
74    }
75
76    #[test]
77    fn test_format_size_gb() {
78        assert_eq!(format_size(1024 * 1024 * 1024), "1.0 GB");
79    }
80
81    #[test]
82    fn test_format_size_iec() {
83        assert_eq!(format_size_iec(0), "0 B");
84        assert_eq!(format_size_iec(1024), "1.00 KiB");
85        assert_eq!(format_size_iec(1024 * 1024), "1.00 MiB");
86        assert_eq!(format_size_iec(1024 * 1024 * 1024), "1.00 GiB");
87    }
88
89    #[test]
90    fn test_format_size_compact() {
91        assert_eq!(format_size_compact(0), "0B");
92        assert_eq!(format_size_compact(1024), "1K");
93        assert_eq!(format_size_compact(1024 * 1024), "1.0M");
94        assert_eq!(format_size_compact(1024 * 1024 * 1024), "1.0G");
95    }
96}