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}