Skip to main content

thoughts_tool/mount/
manager.rs

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