Skip to main content

disk_core/
display.rs

1//! Small, filesystem-neutral formatters used when showing entries to a person.
2
3/// Format a byte count as a short human-readable size (`512 B`, `1.5 MiB`, …).
4pub fn format_size(bytes: u64) -> String {
5    const KIB: u64 = 1024;
6    const MIB: u64 = 1024 * KIB;
7    const GIB: u64 = 1024 * MIB;
8    const TIB: u64 = 1024 * GIB;
9
10    if bytes >= TIB {
11        format!("{:.1} TiB", bytes as f64 / TIB as f64)
12    } else if bytes >= GIB {
13        format!("{:.1} GiB", bytes as f64 / GIB as f64)
14    } else if bytes >= MIB {
15        format!("{:.1} MiB", bytes as f64 / MIB as f64)
16    } else if bytes >= KIB {
17        format!("{:.1} KiB", bytes as f64 / KIB as f64)
18    } else {
19        format!("{bytes} B")
20    }
21}
22
23/// Render a Mac four-character `OSType` lossily: non-printable bytes become `.`.
24///
25/// Display-only (e.g. `PoƒP` = `50 6f C4 50` shows as `Po.P`); carry the raw
26/// `[u8; 4]` wherever fidelity matters.
27pub fn decode_ostype(code: &[u8; 4]) -> String {
28    code.iter()
29        .map(|&b| {
30            if b.is_ascii_graphic() || b == b' ' {
31                b as char
32            } else {
33                '.'
34            }
35        })
36        .collect()
37}
38
39const S_IFMT: u32 = 0o170000;
40const S_IFSOCK: u32 = 0o140000;
41const S_IFLNK: u32 = 0o120000;
42const S_IFBLK: u32 = 0o060000;
43const S_IFDIR: u32 = 0o040000;
44const S_IFCHR: u32 = 0o020000;
45const S_IFIFO: u32 = 0o010000;
46const S_ISUID: u32 = 0o4000;
47const S_ISGID: u32 = 0o2000;
48const S_ISVTX: u32 = 0o1000;
49
50/// Render raw Unix mode bits as a 10-character permission string (e.g. `drwxr-xr-x`).
51pub fn unix_mode_string(mode: u32) -> String {
52    let file_type_char = match mode & S_IFMT {
53        S_IFDIR => 'd',
54        S_IFLNK => 'l',
55        S_IFBLK => 'b',
56        S_IFCHR => 'c',
57        S_IFIFO => 'p',
58        S_IFSOCK => 's',
59        _ => '-',
60    };
61    let mut chars = ['-'; 9];
62    // Owner
63    if mode & 0o400 != 0 {
64        chars[0] = 'r';
65    }
66    if mode & 0o200 != 0 {
67        chars[1] = 'w';
68    }
69    if mode & 0o100 != 0 {
70        chars[2] = if mode & S_ISUID != 0 { 's' } else { 'x' };
71    } else if mode & S_ISUID != 0 {
72        chars[2] = 'S';
73    }
74    // Group
75    if mode & 0o040 != 0 {
76        chars[3] = 'r';
77    }
78    if mode & 0o020 != 0 {
79        chars[4] = 'w';
80    }
81    if mode & 0o010 != 0 {
82        chars[5] = if mode & S_ISGID != 0 { 's' } else { 'x' };
83    } else if mode & S_ISGID != 0 {
84        chars[5] = 'S';
85    }
86    // Other
87    if mode & 0o004 != 0 {
88        chars[6] = 'r';
89    }
90    if mode & 0o002 != 0 {
91        chars[7] = 'w';
92    }
93    if mode & 0o001 != 0 {
94        chars[8] = if mode & S_ISVTX != 0 { 't' } else { 'x' };
95    } else if mode & S_ISVTX != 0 {
96        chars[8] = 'T';
97    }
98    let mut s = String::with_capacity(10);
99    s.push(file_type_char);
100    s.extend(chars.iter());
101    s
102}