Skip to main content

thoughts_tool/mount/
manager.rs

1use super::types::MountInfo;
2use super::types::MountOptions;
3use crate::error::Result;
4use crate::platform::Platform;
5use crate::platform::PlatformInfo;
6use async_trait::async_trait;
7use std::path::Path;
8use std::path::PathBuf;
9
10#[cfg(target_os = "linux")]
11use super::mergerfs::MergerfsManager;
12
13#[cfg(target_os = "macos")]
14use super::fuse_t::FuseTManager;
15
16/// Trait for platform-specific mount operations
17#[async_trait]
18pub trait MountManager: Send + Sync {
19    /// Mount multiple source directories to a target directory
20    async fn mount(&self, sources: &[PathBuf], target: &Path, options: &MountOptions)
21    -> Result<()>;
22
23    /// Unmount a target directory
24    async fn unmount(&self, target: &Path, force: bool) -> Result<()>;
25
26    /// Check if a path is currently mounted
27    async fn is_mounted(&self, target: &Path) -> Result<bool>;
28
29    /// List all active mounts managed by this tool
30    async fn list_mounts(&self) -> Result<Vec<MountInfo>>;
31
32    /// Get detailed information about a specific mount
33    async fn get_mount_info(&self, target: &Path) -> Result<Option<MountInfo>>;
34
35    /// Check if the mount system is available and properly configured
36    async fn check_health(&self) -> Result<()>;
37
38    /// Get platform-specific mount command for debugging
39    fn get_mount_command(
40        &self,
41        sources: &[PathBuf],
42        target: &Path,
43        options: &MountOptions,
44    ) -> String;
45}
46
47/// Factory function to get the appropriate mount manager for the current platform
48pub fn get_mount_manager(platform_info: &PlatformInfo) -> Result<Box<dyn MountManager>> {
49    match &platform_info.platform {
50        #[cfg(target_os = "linux")]
51        Platform::Linux(info) => {
52            if !info.has_mergerfs {
53                return Err(crate::error::ThoughtsError::ToolNotFound {
54                    tool: "mergerfs".to_string(),
55                });
56            }
57            if !info.fuse_available {
58                return Err(crate::error::ThoughtsError::PlatformNotSupported {
59                    platform: "Linux without FUSE support".to_string(),
60                });
61            }
62            Ok(Box::new(MergerfsManager::new(info.clone())))
63        }
64        #[cfg(target_os = "macos")]
65        Platform::MacOS(info) => {
66            if !info.has_fuse_t && !info.has_macfuse {
67                return Err(crate::error::ThoughtsError::ToolNotFound {
68                    tool: "FUSE-T or macFUSE".to_string(),
69                });
70            }
71            Ok(Box::new(FuseTManager::new(info.clone())))
72        }
73        #[cfg(not(target_os = "linux"))]
74        Platform::Linux(_) => Err(crate::error::ThoughtsError::PlatformNotSupported {
75            platform: "Linux support not compiled in".to_string(),
76        }),
77        #[cfg(not(target_os = "macos"))]
78        Platform::MacOS(_) => Err(crate::error::ThoughtsError::PlatformNotSupported {
79            platform: "macOS support not compiled in".to_string(),
80        }),
81        Platform::Unsupported(os) => Err(crate::error::ThoughtsError::PlatformNotSupported {
82            platform: os.clone(),
83        }),
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    #[cfg(any(target_os = "linux", target_os = "macos"))]
90    use super::get_mount_manager;
91    #[cfg(any(target_os = "linux", target_os = "macos"))]
92    use crate::platform::Platform;
93    #[cfg(any(target_os = "linux", target_os = "macos"))]
94    use crate::platform::PlatformInfo;
95    #[cfg(target_os = "linux")]
96    use crate::platform::detector::LinuxInfo;
97    #[cfg(target_os = "macos")]
98    use crate::platform::detector::MacOSInfo;
99    #[cfg(any(target_os = "linux", target_os = "macos"))]
100    use std::path::PathBuf;
101
102    #[test]
103    #[cfg(target_os = "linux")]
104    fn test_get_mount_manager_linux() {
105        let platform_info = PlatformInfo {
106            platform: Platform::Linux(LinuxInfo {
107                distro: "Ubuntu".to_string(),
108                version: "22.04".to_string(),
109                has_mergerfs: true,
110                mergerfs_version: Some("2.33.5".to_string()),
111                fuse_available: true,
112                has_fusermount: false, // Testing without fusermount - should still work
113                mergerfs_path: None,
114                fusermount_path: None,
115            }),
116            arch: "x86_64".to_string(),
117        };
118
119        let manager = get_mount_manager(&platform_info);
120        assert!(manager.is_ok());
121    }
122
123    #[test]
124    #[cfg(target_os = "linux")]
125    fn test_get_mount_manager_missing_tools() {
126        let platform_info = PlatformInfo {
127            platform: Platform::Linux(LinuxInfo {
128                distro: "Ubuntu".to_string(),
129                version: "22.04".to_string(),
130                has_mergerfs: false,
131                mergerfs_version: None,
132                fuse_available: true,
133                has_fusermount: true, // Even with fusermount, can't mount without mergerfs
134                mergerfs_path: None,
135                fusermount_path: Some(PathBuf::from("/bin/fusermount")),
136            }),
137            arch: "x86_64".to_string(),
138        };
139
140        let result = get_mount_manager(&platform_info);
141        assert!(result.is_err());
142        if let Err(e) = result {
143            match e {
144                crate::error::ThoughtsError::ToolNotFound { tool } => {
145                    assert_eq!(tool, "mergerfs");
146                }
147                _ => panic!("Expected ToolNotFound error"),
148            }
149        }
150    }
151
152    #[test]
153    #[cfg(target_os = "macos")]
154    fn test_get_mount_manager_macos() {
155        let platform_info = PlatformInfo {
156            platform: Platform::MacOS(MacOSInfo {
157                version: "14.0".to_string(),
158                has_fuse_t: true,
159                fuse_t_version: Some("1.0.0".to_string()),
160                has_macfuse: false,
161                macfuse_version: None,
162                has_unionfs: true,
163                unionfs_path: Some(PathBuf::from("/usr/local/bin/unionfs-fuse")),
164            }),
165            arch: "aarch64".to_string(),
166        };
167
168        let manager = get_mount_manager(&platform_info);
169        assert!(manager.is_ok());
170    }
171
172    #[test]
173    #[cfg(target_os = "macos")]
174    fn test_get_mount_manager_no_fuse() {
175        let platform_info = PlatformInfo {
176            platform: Platform::MacOS(MacOSInfo {
177                version: "14.0".to_string(),
178                has_fuse_t: false,
179                fuse_t_version: None,
180                has_macfuse: false,
181                macfuse_version: None,
182                has_unionfs: false,
183                unionfs_path: None,
184            }),
185            arch: "aarch64".to_string(),
186        };
187
188        let result = get_mount_manager(&platform_info);
189        assert!(result.is_err());
190        if let Err(e) = result {
191            match e {
192                crate::error::ThoughtsError::ToolNotFound { tool } => {
193                    assert_eq!(tool, "FUSE-T or macFUSE");
194                }
195                _ => panic!("Expected ToolNotFound error"),
196            }
197        }
198    }
199}