file manager
git clone https://git.lucas.co/cce-files.git
src/util.rs (2.9K)
1 //! Small formatting helpers shared across pages and services.
2
3 /// Format a byte count as a short human-readable size (e.g. `1.5 M`).
4 pub fn format_size(size: u64) -> String {
5 if size < 1024 {
6 format!("{} B", size)
7 } else if size < 1024 * 1024 {
8 format!("{:.1} K", size as f64 / 1024.0)
9 } else if size < 1024 * 1024 * 1024 {
10 format!("{:.1} M", size as f64 / (1024.0 * 1024.0))
11 } else {
12 format!("{:.1} G", size as f64 / (1024.0 * 1024.0 * 1024.0))
13 }
14 }
15
16 /// Format a Unix mode into a 10-character `drwxr-xr-x`-style permission string.
17 pub fn format_permissions(mode: u32) -> String {
18 let mut s = String::with_capacity(10);
19 s.push(if mode & 0o40000 != 0 { 'd' } else { '-' });
20 s.push(if mode & 0o400 != 0 { 'r' } else { '-' });
21 s.push(if mode & 0o200 != 0 { 'w' } else { '-' });
22 s.push(if mode & 0o100 != 0 { 'x' } else { '-' });
23 s.push(if mode & 0o040 != 0 { 'r' } else { '-' });
24 s.push(if mode & 0o020 != 0 { 'w' } else { '-' });
25 s.push(if mode & 0o010 != 0 { 'x' } else { '-' });
26 s.push(if mode & 0o004 != 0 { 'r' } else { '-' });
27 s.push(if mode & 0o002 != 0 { 'w' } else { '-' });
28 s.push(if mode & 0o001 != 0 { 'x' } else { '-' });
29 s
30 }
31
32 #[cfg(test)]
33 mod tests {
34 use super::*;
35
36 #[test]
37 fn format_size_units() {
38 assert_eq!(format_size(0), "0 B");
39 assert_eq!(format_size(512), "512 B");
40 assert_eq!(format_size(1024), "1.0 K");
41 assert_eq!(format_size(1048576), "1.0 M");
42 assert_eq!(format_size(1073741824), "1.0 G");
43 }
44
45 #[test]
46 fn format_permissions_string() {
47 assert_eq!(format_permissions(0o40755), "drwxr-xr-x");
48 assert_eq!(format_permissions(0o100644), "-rw-r--r--");
49 assert_eq!(format_permissions(0o644), "-rw-r--r--");
50 }
51 }
52
53 /// Truncate `s` to fit `avail` px, measured for real (resvg-backed, cached per
54 /// string+size — the handful of details strings re-measure only on selection
55 /// change). `head` replaces the front ("...ail/of/path"), else the back
56 /// ("name..."). Binary search on kept chars: ~7 probes for a long path.
57 pub fn truncate_px(s: &str, family: &str, size: f32, avail: f32, head: bool) -> String {
58 if cce_ui::widget::display::measure_text_width(s, family, size) <= avail {
59 return s.to_string();
60 }
61 let chars: Vec<char> = s.chars().collect();
62 let build = |keep: usize| -> String {
63 if head {
64 let tail: String = chars[chars.len() - keep..].iter().collect();
65 format!("...{tail}")
66 } else {
67 let kept: String = chars[..keep].iter().collect();
68 format!("{kept}...")
69 }
70 };
71 let (mut lo, mut hi) = (0usize, chars.len().saturating_sub(1));
72 while lo < hi {
73 let mid = (lo + hi + 1) / 2;
74 if cce_ui::widget::display::measure_text_width(&build(mid), family, size) <= avail {
75 lo = mid;
76 } else {
77 hi = mid - 1;
78 }
79 }
80 build(lo)
81 }