cageforge_policy/
context.rs1use crate::PathSelector;
10use crate::PolicyError;
11use cageforge_path::NativePathKey;
12use std::collections::HashSet;
13use std::path::Path;
14use std::path::PathBuf;
15
16#[derive(Debug, Clone, Default, PartialEq, Eq)]
21pub struct PathResolutionContext {
22 root_paths: Vec<PathBuf>,
23 root_keys: HashSet<NativePathKey>,
24 workspace_roots: Vec<PathBuf>,
25 workspace_root_keys: HashSet<NativePathKey>,
26 minimal_paths: Vec<PathBuf>,
27 minimal_path_keys: HashSet<NativePathKey>,
28 executable_roots: Vec<PathBuf>,
29 executable_root_keys: HashSet<NativePathKey>,
30 tmpdir: Option<PathBuf>,
31 slash_tmp: Option<PathBuf>,
32 current_directory: Option<PathBuf>,
33}
34
35impl PathResolutionContext {
36 pub fn new() -> Self {
38 Self {
39 root_paths: Vec::new(),
40 root_keys: HashSet::new(),
41 workspace_roots: Vec::new(),
42 workspace_root_keys: HashSet::new(),
43 minimal_paths: Vec::new(),
44 minimal_path_keys: HashSet::new(),
45 executable_roots: Vec::new(),
46 executable_root_keys: HashSet::new(),
47 tmpdir: None,
48 slash_tmp: None,
49 current_directory: None,
50 }
51 }
52
53 pub fn with_root(mut self, path: impl Into<PathBuf>) -> Result<Self, PolicyError> {
59 let path = validated_absolute(path.into())?;
60 if self.root_keys.insert(NativePathKey::new(&path)) {
61 self.root_paths.push(path);
62 }
63 Ok(self)
64 }
65
66 pub fn with_workspace_root(mut self, path: impl Into<PathBuf>) -> Result<Self, PolicyError> {
68 let path = validated_absolute(path.into())?;
69 if self.workspace_root_keys.insert(NativePathKey::new(&path)) {
70 self.workspace_roots.push(path);
71 }
72 Ok(self)
73 }
74
75 pub fn with_minimal_path(mut self, path: impl Into<PathBuf>) -> Result<Self, PolicyError> {
77 let path = validated_absolute(path.into())?;
78 if self.minimal_path_keys.insert(NativePathKey::new(&path)) {
79 self.minimal_paths.push(path);
80 }
81 Ok(self)
82 }
83
84 pub fn with_executable_root(mut self, path: impl Into<PathBuf>) -> Result<Self, PolicyError> {
91 let path = validated_absolute(path.into())?;
92 if self.executable_root_keys.insert(NativePathKey::new(&path)) {
93 self.executable_roots.push(path);
94 }
95 Ok(self)
96 }
97
98 pub fn with_tmpdir(mut self, path: impl Into<PathBuf>) -> Result<Self, PolicyError> {
100 self.tmpdir = Some(validated_absolute(path.into())?);
101 Ok(self)
102 }
103
104 pub fn with_slash_tmp(mut self, path: impl Into<PathBuf>) -> Result<Self, PolicyError> {
106 self.slash_tmp = Some(validated_absolute(path.into())?);
107 Ok(self)
108 }
109
110 pub fn with_current_directory(mut self, path: impl Into<PathBuf>) -> Result<Self, PolicyError> {
116 self.current_directory = Some(validated_absolute(path.into())?);
117 Ok(self)
118 }
119
120 pub fn workspace_roots(&self) -> &[PathBuf] {
122 &self.workspace_roots
123 }
124
125 pub fn root_paths(&self) -> &[PathBuf] {
127 &self.root_paths
128 }
129
130 pub fn minimal_paths(&self) -> &[PathBuf] {
132 &self.minimal_paths
133 }
134
135 pub fn executable_roots(&self) -> &[PathBuf] {
137 &self.executable_roots
138 }
139
140 pub fn tmpdir(&self) -> Option<&Path> {
142 self.tmpdir.as_deref()
143 }
144
145 pub fn slash_tmp(&self) -> Option<&Path> {
147 self.slash_tmp.as_deref()
148 }
149
150 pub fn current_directory(&self) -> Option<&Path> {
153 self.current_directory.as_deref()
154 }
155}
156
157fn validated_absolute(path: PathBuf) -> Result<PathBuf, PolicyError> {
158 PathSelector::absolute(path)?
159 .path()
160 .map(Path::to_path_buf)
161 .ok_or_else(|| PolicyError::InvalidContext {
162 message: "absolute path validation returned a non-absolute selector".to_string(),
163 })
164}