1use std::path::Path;
2
3#[cfg(test)]
4mod test;
5
6pub struct DiskSpaceForHuman {
8 pub available: f64,
9 pub total: f64,
10}
11
12pub struct DiskSpace {
14 pub available: u64,
15 pub total: u64,
16}
17
18impl DiskSpace {
19 pub fn new(path: &Path) -> Result<DiskSpace, std::io::Error> {
21 #[cfg(not(windows))]
22 {
23 use std::ffi::CString;
24
25 let c_path = CString::new(path.to_str().unwrap()).unwrap();
26 let mut stat = unsafe { std::mem::zeroed::<libc::statvfs>() };
27 let ret = unsafe { libc::statvfs(c_path.as_ptr(), &mut stat) };
28 if ret != 0 {
29 return Err(std::io::Error::last_os_error());
30 }
31 Ok(DiskSpace {
32 available: stat.f_bavail as u64 * stat.f_frsize as u64,
33 total: stat.f_blocks as u64 * stat.f_frsize as u64,
34 })
35 }
36
37 #[cfg(windows)]
38 {
39 use std::os::windows::ffi::OsStrExt;
40 let wide: Vec<u16> = path.as_os_str().encode_wide().chain([0]).collect();
41 let (mut free, mut total, mut tf) = (0u64, 0u64, 0u64);
42
43 #[link(name = "kernel32")]
44 unsafe extern "system" {
45 fn GetDiskFreeSpaceExW(
46 path: *const u16,
47 free: *mut u64,
48 total: *mut u64,
49 total_free: *mut u64,
50 ) -> i32;
51 }
52 let ok = unsafe { GetDiskFreeSpaceExW(wide.as_ptr(), &mut free, &mut total, &mut tf) };
53
54 if ok == 0 {
55 return Err(std::io::Error::last_os_error());
56 }
57 Ok(DiskSpace {
58 available: free,
59 total,
60 })
61 }
62 }
63
64 pub fn as_mb(&self) -> DiskSpaceForHuman {
66 let divisor = 1024u64.pow(2) as f64;
67 let (available, total) = (self.available as f64 / divisor, self.total as f64 / divisor);
68 DiskSpaceForHuman { available, total }
69 }
70
71 pub fn as_gb(&self) -> DiskSpaceForHuman {
73 let divisor = 1024u64.pow(3) as f64;
74 let (available, total) = (self.available as f64 / divisor, self.total as f64 / divisor);
75 DiskSpaceForHuman { available, total }
76 }
77}