Skip to main content

smriti/services/
drive_detector.rs

1//! Drive detection service
2//!
3//! Detects mounted external drives and directories that can be indexed.
4
5use serde::{Deserialize, Serialize};
6use std::path::PathBuf;
7
8/// Information about a detected drive or folder
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct DriveInfo {
11    pub name: String,
12    pub path: PathBuf,
13    pub is_removable: bool,
14    pub has_photovault_db: bool,
15    pub total_size_bytes: Option<u64>,
16    pub free_space_bytes: Option<u64>,
17}
18
19/// Drive detection service
20pub struct DriveDetector;
21
22impl DriveDetector {
23    /// Detect available drives/mount points
24    pub fn detect() -> Vec<DriveInfo> {
25        let mut drives = Vec::new();
26
27        #[cfg(target_os = "linux")]
28        {
29            // Check /media and /mnt for mounted drives
30            for base in &["/media", "/mnt", "/run/media"] {
31                if let Ok(entries) = std::fs::read_dir(base) {
32                    for entry in entries.flatten() {
33                        // For /media and /run/media, we need to go one level deeper (user folder)
34                        if *base == "/media" || base.starts_with("/run/media") {
35                            if let Ok(user_entries) = std::fs::read_dir(entry.path()) {
36                                for user_entry in user_entries.flatten() {
37                                    if let Some(drive) = Self::check_path(user_entry.path()) {
38                                        drives.push(drive);
39                                    }
40                                }
41                            }
42                        } else if let Some(drive) = Self::check_path(entry.path()) {
43                            drives.push(drive);
44                        }
45                    }
46                }
47            }
48
49            // Also check home directory as a valid target
50            if let Some(home) = dirs::home_dir() {
51                if let Some(drive) = Self::check_path(home.join("Pictures")) {
52                    drives.push(drive);
53                }
54            }
55        }
56
57        #[cfg(target_os = "macos")]
58        {
59            // Check /Volumes for mounted drives
60            if let Ok(entries) = std::fs::read_dir("/Volumes") {
61                for entry in entries.flatten() {
62                    if let Some(drive) = Self::check_path(entry.path()) {
63                        drives.push(drive);
64                    }
65                }
66            }
67        }
68
69        #[cfg(target_os = "windows")]
70        {
71            // Check drive letters
72            for letter in 'A'..='Z' {
73                let path = PathBuf::from(format!("{}:\\", letter));
74                if path.exists() {
75                    if let Some(drive) = Self::check_path(path) {
76                        drives.push(drive);
77                    }
78                }
79            }
80        }
81
82        drives
83    }
84
85    /// Inspect a user-provided folder path and return DriveInfo if valid.
86    pub fn inspect_path(path: PathBuf) -> Option<DriveInfo> {
87        Self::check_path(path)
88    }
89
90    /// Check if a path is a valid drive/folder for indexing
91    fn check_path(path: PathBuf) -> Option<DriveInfo> {
92        if !path.exists() || !path.is_dir() {
93            return None;
94        }
95
96        // Check if it's readable
97        if std::fs::read_dir(&path).is_err() {
98            return None;
99        }
100
101        let name = path
102            .file_name()
103            .map(|n| n.to_string_lossy().to_string())
104            .unwrap_or_else(|| path.to_string_lossy().to_string());
105
106        let has_photovault_db = path.join(".photovault").join("photovault.db").exists();
107
108        Some(DriveInfo {
109            name,
110            path,
111            is_removable: true, // Simplified - could detect properly
112            has_photovault_db,
113            total_size_bytes: None, // Could use platform-specific APIs
114            free_space_bytes: None,
115        })
116    }
117}