/ crates / acdc-check / src / storage.rs
storage.rs
 1  //! Storage detection and information.
 2  
 3  use acdc_core::Result;
 4  use serde::{Deserialize, Serialize};
 5  use sysinfo::Disks;
 6  
 7  /// Storage information.
 8  #[derive(Debug, Clone, Serialize, Deserialize)]
 9  pub struct StorageInfo {
10      /// Total storage in bytes (on the root/home partition)
11      pub total_bytes: u64,
12      /// Available storage in bytes
13      pub available_bytes: u64,
14      /// Mount point checked
15      pub mount_point: String,
16      /// Filesystem type
17      pub fs_type: String,
18      /// Whether storage is likely NVMe/SSD
19      pub is_ssd: bool,
20  }
21  
22  impl StorageInfo {
23      /// Detect storage information for the home directory.
24      pub fn detect() -> Result<Self> {
25          let home = dirs::home_dir().unwrap_or_else(|| std::path::PathBuf::from("/"));
26          let disks = Disks::new_with_refreshed_list();
27  
28          // Find the disk that contains the home directory
29          let disk = disks
30              .iter()
31              .filter(|d| home.starts_with(d.mount_point()))
32              .max_by_key(|d| d.mount_point().as_os_str().len())
33              .or_else(|| {
34                  disks
35                      .iter()
36                      .find(|d| d.mount_point() == std::path::Path::new("/"))
37              });
38  
39          match disk {
40              Some(d) => {
41                  let mount_str = d.mount_point().to_string_lossy().to_string();
42                  let fs_type = d.file_system().to_string_lossy().to_string();
43  
44                  // Heuristic: NVMe devices usually have "nvme" in their name
45                  let is_ssd = d.name().to_string_lossy().contains("nvme")
46                      || d.name().to_string_lossy().contains("ssd");
47  
48                  Ok(Self {
49                      total_bytes: d.total_space(),
50                      available_bytes: d.available_space(),
51                      mount_point: mount_str,
52                      fs_type,
53                      is_ssd,
54                  })
55              }
56              None => Ok(Self {
57                  total_bytes: 0,
58                  available_bytes: 0,
59                  mount_point: "/".to_string(),
60                  fs_type: "unknown".to_string(),
61                  is_ssd: false,
62              }),
63          }
64      }
65  
66      /// Get total storage in GB.
67      pub fn total_gb(&self) -> u64 {
68          self.total_bytes / (1024 * 1024 * 1024)
69      }
70  
71      /// Get available storage in GB.
72      pub fn available_gb(&self) -> u64 {
73          self.available_bytes / (1024 * 1024 * 1024)
74      }
75  }