Skip to main content

thoughts_tool/mount/
resolver.rs

1use crate::config::Mount;
2use crate::config::RepoMappingManager;
3use crate::mount::utils::normalize_mount_path;
4use anyhow::Result;
5use std::collections::HashMap;
6use std::path::PathBuf;
7
8pub struct MountResolver {
9    repo_mapping: RepoMappingManager,
10}
11
12impl MountResolver {
13    pub fn new() -> Result<Self> {
14        Ok(Self {
15            repo_mapping: RepoMappingManager::new()?,
16        })
17    }
18
19    /// Resolve a mount to its local filesystem path
20    pub fn resolve_mount(&self, mount: &Mount) -> Result<PathBuf> {
21        match mount {
22            Mount::Directory { path, .. } => {
23                // Directory mounts need path normalization
24                Ok(normalize_mount_path(path)?)
25            }
26            Mount::Git { url, subpath, .. } => {
27                // Build full URL with subpath if present
28                let full_url = if let Some(sub) = subpath {
29                    format!("{url}:{sub}")
30                } else {
31                    url.clone()
32                };
33
34                // Resolve through mapping
35                self.repo_mapping.resolve_url(&full_url)?.ok_or_else(|| {
36                    use colored::Colorize;
37                    anyhow::anyhow!(
38                        "Repository not cloned: {}\n\n\
39                             To fix this, run one of:\n  \
40                             • {} (auto-managed)\n  \
41                             • {} (custom location)",
42                        url,
43                        format!("thoughts mount clone {url}").cyan(),
44                        format!("thoughts mount clone {url} /your/path").cyan()
45                    )
46                })
47            }
48        }
49    }
50
51    /// Check if a mount needs cloning
52    pub fn needs_clone(&self, mount: &Mount) -> Result<bool> {
53        match mount {
54            Mount::Directory { .. } => Ok(false),
55            Mount::Git { url, .. } => Ok(self.repo_mapping.resolve_url(url)?.is_none()),
56        }
57    }
58
59    /// Get clone URL and suggested path for a mount
60    // TODO(2): Integrate into clone command for consistency
61    pub fn get_clone_info(&self, mount: &Mount) -> Result<Option<(String, PathBuf)>> {
62        match mount {
63            Mount::Directory { .. } => Ok(None),
64            Mount::Git { url, .. } => {
65                if self.needs_clone(mount)? {
66                    let clone_path = RepoMappingManager::get_default_clone_path(url)?;
67                    Ok(Some((url.clone(), clone_path)))
68                } else {
69                    Ok(None)
70                }
71            }
72        }
73    }
74
75    /// Resolve all mounts in a config
76    // TODO(2): Keep for future batch operations and diagnostics
77    pub fn resolve_all(&self, mounts: &HashMap<String, Mount>) -> Vec<(String, Result<PathBuf>)> {
78        mounts
79            .iter()
80            .map(|(name, mount)| (name.clone(), self.resolve_mount(mount)))
81            .collect()
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88    use crate::config::SyncStrategy;
89
90    #[test]
91    fn test_directory_resolution() {
92        let resolver = MountResolver::new().unwrap();
93        let mount = Mount::Directory {
94            path: PathBuf::from("/home/user/docs"),
95            sync: SyncStrategy::None,
96        };
97
98        let result_path = resolver.resolve_mount(&mount).unwrap();
99        assert_eq!(result_path, PathBuf::from("/home/user/docs"));
100    }
101
102    #[test]
103    fn test_git_mount_detection() {
104        let mount = Mount::Git {
105            url: "git@github.com:test/repo.git".to_string(),
106            sync: SyncStrategy::Auto,
107            subpath: None,
108        };
109
110        // Test that we can detect git mounts
111        assert!(mount.is_git());
112    }
113}