ralph_workflow/config/path_resolver/
memory_env.rs1use std::collections::HashMap;
2use std::io;
3use std::path::{Path, PathBuf};
4use std::sync::{Arc, RwLock};
5
6use super::ConfigEnvironment;
7
8#[derive(Debug, Clone, Default)]
34pub struct MemoryConfigEnvironment {
35 unified_config_path: Option<PathBuf>,
36 prompt_path: Option<PathBuf>,
37 local_config_path: Option<PathBuf>,
38 worktree_root: Option<PathBuf>,
39 files: Arc<RwLock<HashMap<PathBuf, String>>>,
41 dirs: Arc<RwLock<std::collections::HashSet<PathBuf>>>,
43 env_vars: HashMap<String, String>,
48}
49
50impl MemoryConfigEnvironment {
51 #[must_use]
53 pub fn new() -> Self {
54 Self::default()
55 }
56
57 #[must_use]
59 pub fn with_unified_config_path<P: Into<PathBuf>>(mut self, path: P) -> Self {
60 self.unified_config_path = Some(path.into());
61 self
62 }
63
64 #[must_use]
66 pub fn with_local_config_path<P: Into<PathBuf>>(mut self, path: P) -> Self {
67 self.local_config_path = Some(path.into());
68 self
69 }
70
71 #[must_use]
73 pub fn with_prompt_path<P: Into<PathBuf>>(mut self, path: P) -> Self {
74 self.prompt_path = Some(path.into());
75 self
76 }
77
78 #[must_use]
84 pub fn with_file<P: Into<PathBuf>, S: Into<String>>(self, path: P, content: S) -> Self {
85 let path = path.into();
86 self.files.write()
87 .expect("RwLock poisoned - indicates panic in another thread holding MemoryConfigEnvironment files lock")
88 .insert(path, content.into());
89 self
90 }
91
92 #[must_use]
94 pub fn with_worktree_root<P: Into<PathBuf>>(mut self, path: P) -> Self {
95 self.worktree_root = Some(path.into());
96 self
97 }
98
99 #[must_use]
112 pub fn with_env_var(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
113 self.env_vars.insert(key.into(), value.into());
114 self
115 }
116
117 #[must_use]
123 pub fn get_file(&self, path: &Path) -> Option<String> {
124 self.files.read()
125 .expect("RwLock poisoned - indicates panic in another thread holding MemoryConfigEnvironment files lock")
126 .get(path).cloned()
127 }
128
129 #[must_use]
135 pub fn was_written(&self, path: &Path) -> bool {
136 self.files.read()
137 .expect("RwLock poisoned - indicates panic in another thread holding MemoryConfigEnvironment files lock")
138 .contains_key(path)
139 }
140}
141
142impl ConfigEnvironment for MemoryConfigEnvironment {
143 fn unified_config_path(&self) -> Option<PathBuf> {
144 self.unified_config_path.clone()
145 }
146
147 fn get_env_var(&self, key: &str) -> Option<String> {
152 self.env_vars.get(key).cloned()
153 }
154
155 fn local_config_path(&self) -> Option<PathBuf> {
156 if let Some(ref path) = self.local_config_path {
158 return Some(path.clone());
159 }
160
161 self.worktree_root()
163 .map(|root| root.join(".agent/ralph-workflow.toml"))
164 .or_else(|| Some(PathBuf::from(".agent/ralph-workflow.toml")))
165 }
166
167 fn prompt_path(&self) -> PathBuf {
168 self.prompt_path
169 .clone()
170 .unwrap_or_else(|| PathBuf::from("PROMPT.md"))
171 }
172
173 fn file_exists(&self, path: &Path) -> bool {
174 self.files.read()
175 .expect("RwLock poisoned - indicates panic in another thread holding MemoryConfigEnvironment files lock")
176 .contains_key(path)
177 }
178
179 fn read_file(&self, path: &Path) -> io::Result<String> {
180 self.files
181 .read()
182 .expect("RwLock poisoned - indicates panic in another thread holding MemoryConfigEnvironment files lock")
183 .get(path)
184 .cloned()
185 .ok_or_else(|| {
186 io::Error::new(
187 io::ErrorKind::NotFound,
188 format!("File not found: {}", path.display()),
189 )
190 })
191 }
192
193 fn write_file(&self, path: &Path, content: &str) -> io::Result<()> {
194 if let Some(parent) = path.parent() {
196 self.dirs.write()
197 .expect("RwLock poisoned - indicates panic in another thread holding MemoryConfigEnvironment dirs lock")
198 .insert(parent.to_path_buf());
199 }
200 self.files
201 .write()
202 .expect("RwLock poisoned - indicates panic in another thread holding MemoryConfigEnvironment files lock")
203 .insert(path.to_path_buf(), content.to_string());
204 Ok(())
205 }
206
207 fn create_dir_all(&self, path: &Path) -> io::Result<()> {
208 self.dirs.write()
209 .expect("RwLock poisoned - indicates panic in another thread holding MemoryConfigEnvironment dirs lock")
210 .insert(path.to_path_buf());
211 Ok(())
212 }
213
214 fn worktree_root(&self) -> Option<PathBuf> {
215 self.worktree_root.clone()
216 }
217}