ricecoder_sessions/
context.rs1use crate::error::{SessionError, SessionResult};
4use crate::models::SessionContext;
5
6#[derive(Debug, Clone)]
11pub struct ContextManager {
12 context: Option<SessionContext>,
14}
15
16impl ContextManager {
17 pub fn new() -> Self {
19 Self { context: None }
20 }
21
22 pub fn with_context(context: SessionContext) -> Self {
24 Self {
25 context: Some(context),
26 }
27 }
28
29 pub fn set_context(&mut self, context: SessionContext) {
35 self.context = Some(context);
36 }
37
38 pub fn get_context(&self) -> SessionResult<SessionContext> {
43 self.context
44 .clone()
45 .ok_or_else(|| SessionError::Invalid("No context set".to_string()))
46 }
47
48 fn get_context_mut(&mut self) -> SessionResult<&mut SessionContext> {
52 self.context
53 .as_mut()
54 .ok_or_else(|| SessionError::Invalid("No context set".to_string()))
55 }
56
57 pub fn add_file(&mut self, file_path: String) -> SessionResult<()> {
62 let context = self.get_context_mut()?;
63
64 if !context.files.contains(&file_path) {
66 context.files.push(file_path);
67 }
68
69 Ok(())
70 }
71
72 pub fn remove_file(&mut self, file_path: &str) -> SessionResult<()> {
77 let context = self.get_context_mut()?;
78 context.files.retain(|f| f != file_path);
79 Ok(())
80 }
81
82 pub fn get_files(&self) -> SessionResult<Vec<String>> {
84 self.get_context().map(|ctx| ctx.files)
85 }
86
87 pub fn clear_files(&mut self) -> SessionResult<()> {
89 let context = self.get_context_mut()?;
90 context.files.clear();
91 Ok(())
92 }
93
94 pub fn set_project_path(&mut self, path: Option<String>) -> SessionResult<()> {
96 let context = self.get_context_mut()?;
97 context.project_path = path;
98 Ok(())
99 }
100
101 pub fn get_project_path(&self) -> SessionResult<Option<String>> {
103 self.get_context().map(|ctx| ctx.project_path)
104 }
105
106 pub fn has_file(&self, file_path: &str) -> SessionResult<bool> {
108 self.get_context()
109 .map(|ctx| ctx.files.contains(&file_path.to_string()))
110 }
111
112 pub fn is_set(&self) -> bool {
114 self.context.is_some()
115 }
116
117 pub fn clear(&mut self) {
119 self.context = None;
120 }
121
122 pub fn switch_project(&mut self, project_path: String) -> SessionResult<()> {
127 let context = self.get_context_mut()?;
128 context.project_path = Some(project_path);
129 context.files.clear();
131 Ok(())
132 }
133
134 pub fn get_context_for_persistence(&self) -> SessionResult<SessionContext> {
139 self.get_context()
140 }
141
142 pub fn restore_from_persistence(&mut self, context: SessionContext) {
147 self.context = Some(context);
148 }
149}
150
151impl Default for ContextManager {
152 fn default() -> Self {
153 Self::new()
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160 use crate::models::SessionMode;
161
162 fn create_test_context() -> SessionContext {
163 SessionContext::new("openai".to_string(), "gpt-4".to_string(), SessionMode::Chat)
164 }
165
166 #[test]
167 fn test_context_manager_new() {
168 let manager = ContextManager::new();
169 assert!(!manager.is_set());
170 }
171
172 #[test]
173 fn test_set_and_get_context() {
174 let mut manager = ContextManager::new();
175 let context = create_test_context();
176
177 manager.set_context(context.clone());
178
179 let retrieved = manager.get_context().unwrap();
180 assert_eq!(retrieved.provider, context.provider);
181 assert_eq!(retrieved.model, context.model);
182 }
183
184 #[test]
185 fn test_add_file() {
186 let mut manager = ContextManager::new();
187 manager.set_context(create_test_context());
188
189 manager.add_file("file1.rs".to_string()).unwrap();
190 manager.add_file("file2.rs".to_string()).unwrap();
191
192 let files = manager.get_files().unwrap();
193 assert_eq!(files.len(), 2);
194 assert!(files.contains(&"file1.rs".to_string()));
195 assert!(files.contains(&"file2.rs".to_string()));
196 }
197
198 #[test]
199 fn test_add_duplicate_file() {
200 let mut manager = ContextManager::new();
201 manager.set_context(create_test_context());
202
203 manager.add_file("file1.rs".to_string()).unwrap();
204 manager.add_file("file1.rs".to_string()).unwrap();
205
206 let files = manager.get_files().unwrap();
207 assert_eq!(files.len(), 1);
208 }
209
210 #[test]
211 fn test_remove_file() {
212 let mut manager = ContextManager::new();
213 manager.set_context(create_test_context());
214
215 manager.add_file("file1.rs".to_string()).unwrap();
216 manager.add_file("file2.rs".to_string()).unwrap();
217
218 manager.remove_file("file1.rs").unwrap();
219
220 let files = manager.get_files().unwrap();
221 assert_eq!(files.len(), 1);
222 assert!(files.contains(&"file2.rs".to_string()));
223 }
224
225 #[test]
226 fn test_context_isolation() {
227 let mut manager1 = ContextManager::new();
228 let mut manager2 = ContextManager::new();
229
230 manager1.set_context(create_test_context());
231 manager2.set_context(create_test_context());
232
233 manager1.add_file("file1.rs".to_string()).unwrap();
234 manager2.add_file("file2.rs".to_string()).unwrap();
235
236 let files1 = manager1.get_files().unwrap();
237 let files2 = manager2.get_files().unwrap();
238
239 assert_eq!(files1.len(), 1);
240 assert_eq!(files2.len(), 1);
241 assert!(files1.contains(&"file1.rs".to_string()));
242 assert!(files2.contains(&"file2.rs".to_string()));
243 }
244
245 #[test]
246 fn test_clear_files() {
247 let mut manager = ContextManager::new();
248 manager.set_context(create_test_context());
249
250 manager.add_file("file1.rs".to_string()).unwrap();
251 manager.add_file("file2.rs".to_string()).unwrap();
252
253 manager.clear_files().unwrap();
254
255 let files = manager.get_files().unwrap();
256 assert_eq!(files.len(), 0);
257 }
258
259 #[test]
260 fn test_project_path() {
261 let mut manager = ContextManager::new();
262 manager.set_context(create_test_context());
263
264 manager
265 .set_project_path(Some("/path/to/project".to_string()))
266 .unwrap();
267
268 let path = manager.get_project_path().unwrap();
269 assert_eq!(path, Some("/path/to/project".to_string()));
270 }
271
272 #[test]
273 fn test_has_file() {
274 let mut manager = ContextManager::new();
275 manager.set_context(create_test_context());
276
277 manager.add_file("file1.rs".to_string()).unwrap();
278
279 assert!(manager.has_file("file1.rs").unwrap());
280 assert!(!manager.has_file("file2.rs").unwrap());
281 }
282
283 #[test]
284 fn test_clear_context() {
285 let mut manager = ContextManager::new();
286 manager.set_context(create_test_context());
287
288 assert!(manager.is_set());
289
290 manager.clear();
291
292 assert!(!manager.is_set());
293 assert!(manager.get_context().is_err());
294 }
295
296 #[test]
297 fn test_operations_without_context() {
298 let mut manager = ContextManager::new();
299
300 assert!(manager.get_context().is_err());
301 assert!(manager.add_file("file.rs".to_string()).is_err());
302 assert!(manager.remove_file("file.rs").is_err());
303 }
304
305 #[test]
306 fn test_switch_project() {
307 let mut manager = ContextManager::new();
308 manager.set_context(create_test_context());
309
310 manager.add_file("file1.rs".to_string()).unwrap();
311 manager.add_file("file2.rs".to_string()).unwrap();
312
313 manager.switch_project("/new/project".to_string()).unwrap();
314
315 let path = manager.get_project_path().unwrap();
316 assert_eq!(path, Some("/new/project".to_string()));
317
318 let files = manager.get_files().unwrap();
320 assert_eq!(files.len(), 0);
321 }
322
323 #[test]
324 fn test_persistence_roundtrip() {
325 let mut manager1 = ContextManager::new();
326 let context = create_test_context();
327 manager1.set_context(context);
328
329 manager1.add_file("file1.rs".to_string()).unwrap();
330 manager1.add_file("file2.rs".to_string()).unwrap();
331 manager1
332 .set_project_path(Some("/project".to_string()))
333 .unwrap();
334
335 let persisted_context = manager1.get_context_for_persistence().unwrap();
337
338 let mut manager2 = ContextManager::new();
340 manager2.restore_from_persistence(persisted_context);
341
342 let restored_context = manager2.get_context().unwrap();
344 assert_eq!(restored_context.provider, "openai");
345 assert_eq!(restored_context.model, "gpt-4");
346 assert_eq!(restored_context.project_path, Some("/project".to_string()));
347
348 let files = manager2.get_files().unwrap();
349 assert_eq!(files.len(), 2);
350 assert!(files.contains(&"file1.rs".to_string()));
351 assert!(files.contains(&"file2.rs".to_string()));
352 }
353
354 #[test]
355 fn test_context_isolation_with_operations() {
356 let mut manager1 = ContextManager::new();
357 let mut manager2 = ContextManager::new();
358
359 manager1.set_context(create_test_context());
360 manager2.set_context(create_test_context());
361
362 manager1.add_file("file1.rs".to_string()).unwrap();
364 manager1
365 .set_project_path(Some("/project1".to_string()))
366 .unwrap();
367
368 manager2.add_file("file2.rs".to_string()).unwrap();
369 manager2
370 .set_project_path(Some("/project2".to_string()))
371 .unwrap();
372
373 let context1 = manager1.get_context().unwrap();
375 let context2 = manager2.get_context().unwrap();
376
377 assert_eq!(context1.project_path, Some("/project1".to_string()));
378 assert_eq!(context2.project_path, Some("/project2".to_string()));
379
380 assert_eq!(context1.files.len(), 1);
381 assert_eq!(context2.files.len(), 1);
382 assert!(context1.files.contains(&"file1.rs".to_string()));
383 assert!(context2.files.contains(&"file2.rs".to_string()));
384 }
385}