vtcode_commons/
paths.rs

1use std::path::{Path, PathBuf};
2
3/// Provides the root directories an application uses to store data.
4pub trait WorkspacePaths: Send + Sync {
5    /// Absolute path to the application's workspace root.
6    fn workspace_root(&self) -> &Path;
7
8    /// Returns the directory where configuration files should be stored.
9    fn config_dir(&self) -> PathBuf;
10
11    /// Returns an optional cache directory for transient data.
12    fn cache_dir(&self) -> Option<PathBuf> {
13        None
14    }
15
16    /// Returns an optional directory for telemetry or log artifacts.
17    fn telemetry_dir(&self) -> Option<PathBuf> {
18        None
19    }
20}
21
22/// Helper trait that adds path resolution helpers on top of [`WorkspacePaths`].
23pub trait PathResolver: WorkspacePaths {
24    /// Resolve a path relative to the workspace root.
25    fn resolve<P>(&self, relative: P) -> PathBuf
26    where
27        P: AsRef<Path>,
28    {
29        self.workspace_root().join(relative)
30    }
31
32    /// Resolve a path within the configuration directory.
33    fn resolve_config<P>(&self, relative: P) -> PathBuf
34    where
35        P: AsRef<Path>,
36    {
37        self.config_dir().join(relative)
38    }
39}
40
41impl<T> PathResolver for T where T: WorkspacePaths + ?Sized {}
42
43/// Enumeration describing the conceptual scope of a file path.
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub enum PathScope {
46    Workspace,
47    Config,
48    Cache,
49    Telemetry,
50}
51
52impl PathScope {
53    /// Returns a human-readable description used in error messages.
54    pub fn description(self) -> &'static str {
55        match self {
56            Self::Workspace => "workspace",
57            Self::Config => "configuration",
58            Self::Cache => "cache",
59            Self::Telemetry => "telemetry",
60        }
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67    use std::path::PathBuf;
68
69    struct StaticPaths {
70        root: PathBuf,
71        config: PathBuf,
72    }
73
74    impl WorkspacePaths for StaticPaths {
75        fn workspace_root(&self) -> &Path {
76            &self.root
77        }
78
79        fn config_dir(&self) -> PathBuf {
80            self.config.clone()
81        }
82
83        fn cache_dir(&self) -> Option<PathBuf> {
84            Some(self.root.join("cache"))
85        }
86    }
87
88    #[test]
89    fn resolves_relative_paths() {
90        let paths = StaticPaths {
91            root: PathBuf::from("/tmp/project"),
92            config: PathBuf::from("/tmp/project/config"),
93        };
94
95        assert_eq!(
96            PathResolver::resolve(&paths, "subdir/file.txt"),
97            PathBuf::from("/tmp/project/subdir/file.txt")
98        );
99        assert_eq!(
100            PathResolver::resolve_config(&paths, "settings.toml"),
101            PathBuf::from("/tmp/project/config/settings.toml")
102        );
103        assert_eq!(paths.cache_dir(), Some(PathBuf::from("/tmp/project/cache")));
104    }
105}