Skip to main content

probe/
disk.rs

1use std::path::Path;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub struct DiskSpace {
5    pub free_bytes: u64,
6    pub total_bytes: u64,
7}
8
9#[cfg(unix)]
10pub fn probe_disk(path: &Path) -> Option<DiskSpace> {
11    use std::ffi::CString;
12    use std::os::unix::ffi::OsStrExt;
13
14    let c_path = CString::new(path.as_os_str().as_bytes()).ok()?;
15    let mut stat: libc::statvfs = unsafe { std::mem::zeroed() };
16    let rc = unsafe { libc::statvfs(c_path.as_ptr(), &mut stat) };
17    if rc != 0 {
18        return None;
19    }
20    let block_size = stat.f_frsize as u64;
21    let free_bytes = block_size.saturating_mul(stat.f_bavail as u64);
22    let total_bytes = block_size.saturating_mul(stat.f_blocks as u64);
23    Some(DiskSpace {
24        free_bytes,
25        total_bytes,
26    })
27}
28
29#[cfg(not(unix))]
30pub fn probe_disk(_path: &Path) -> Option<DiskSpace> {
31    None
32}
33
34#[cfg(test)]
35mod tests {
36    use super::*;
37
38    #[test]
39    fn probes_real_tmp_directory() {
40        let dir = crate::testscratch::dir("disk");
41        let space = probe_disk(dir.path()).expect("statvfs should succeed on a real tmp dir");
42        assert!(space.total_bytes > 0);
43        assert!(space.free_bytes <= space.total_bytes);
44    }
45
46    #[test]
47    fn nonexistent_path_returns_none_without_panic() {
48        let path = Path::new("/no/such/path/pito-probe-test-fixture");
49        assert_eq!(probe_disk(path), None);
50    }
51}