1use std::path::{Path, PathBuf};
2
3pub trait WorkspacePaths: Send + Sync {
5 fn workspace_root(&self) -> &Path;
7
8 fn config_dir(&self) -> PathBuf;
10
11 fn cache_dir(&self) -> Option<PathBuf> {
13 None
14 }
15
16 fn telemetry_dir(&self) -> Option<PathBuf> {
18 None
19 }
20
21 fn scope_for_path(&self, path: &Path) -> PathScope {
30 if path.starts_with(self.workspace_root()) {
31 return PathScope::Workspace;
32 }
33
34 let config_dir = self.config_dir();
35 if path.starts_with(&config_dir) {
36 return PathScope::Config;
37 }
38
39 if let Some(cache_dir) = self.cache_dir() {
40 if path.starts_with(&cache_dir) {
41 return PathScope::Cache;
42 }
43 }
44
45 if let Some(telemetry_dir) = self.telemetry_dir() {
46 if path.starts_with(&telemetry_dir) {
47 return PathScope::Telemetry;
48 }
49 }
50
51 PathScope::Cache
52 }
53}
54
55pub trait PathResolver: WorkspacePaths {
57 fn resolve<P>(&self, relative: P) -> PathBuf
59 where
60 P: AsRef<Path>,
61 {
62 self.workspace_root().join(relative)
63 }
64
65 fn resolve_config<P>(&self, relative: P) -> PathBuf
67 where
68 P: AsRef<Path>,
69 {
70 self.config_dir().join(relative)
71 }
72}
73
74impl<T> PathResolver for T where T: WorkspacePaths + ?Sized {}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub enum PathScope {
79 Workspace,
80 Config,
81 Cache,
82 Telemetry,
83}
84
85impl PathScope {
86 pub fn description(self) -> &'static str {
88 match self {
89 Self::Workspace => "workspace",
90 Self::Config => "configuration",
91 Self::Cache => "cache",
92 Self::Telemetry => "telemetry",
93 }
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100 use std::path::PathBuf;
101
102 struct StaticPaths {
103 root: PathBuf,
104 config: PathBuf,
105 }
106
107 impl WorkspacePaths for StaticPaths {
108 fn workspace_root(&self) -> &Path {
109 &self.root
110 }
111
112 fn config_dir(&self) -> PathBuf {
113 self.config.clone()
114 }
115
116 fn cache_dir(&self) -> Option<PathBuf> {
117 Some(self.root.join("cache"))
118 }
119 }
120
121 #[test]
122 fn resolves_relative_paths() {
123 let paths = StaticPaths {
124 root: PathBuf::from("/tmp/project"),
125 config: PathBuf::from("/tmp/project/config"),
126 };
127
128 assert_eq!(
129 PathResolver::resolve(&paths, "subdir/file.txt"),
130 PathBuf::from("/tmp/project/subdir/file.txt")
131 );
132 assert_eq!(
133 PathResolver::resolve_config(&paths, "settings.toml"),
134 PathBuf::from("/tmp/project/config/settings.toml")
135 );
136 assert_eq!(paths.cache_dir(), Some(PathBuf::from("/tmp/project/cache")));
137 }
138}