ricecoder_agents/domain/
context.rs1use crate::domain::error::{DomainError, DomainResult};
4use crate::domain::models::{Recommendation, SharedContext};
5use std::collections::HashMap;
6use std::sync::{Arc, RwLock};
7
8#[derive(Debug, Clone)]
23pub struct SharedContextManager {
24 context: Arc<RwLock<SharedContext>>,
25 agent_recommendations: Arc<RwLock<HashMap<String, Vec<Recommendation>>>>,
26}
27
28impl SharedContextManager {
29 pub fn new() -> Self {
31 Self {
32 context: Arc::new(RwLock::new(SharedContext::default())),
33 agent_recommendations: Arc::new(RwLock::new(HashMap::new())),
34 }
35 }
36
37 pub fn update_context(&self, key: &str, value: serde_json::Value) -> DomainResult<()> {
44 let mut context = self.context.write().map_err(|e| {
45 DomainError::internal(format!("Failed to acquire write lock: {}", e))
46 })?;
47
48 context.cross_domain_state.insert(key.to_string(), value);
49 Ok(())
50 }
51
52 pub fn get_context(&self, key: &str) -> DomainResult<serde_json::Value> {
62 let context = self.context.read().map_err(|e| {
63 DomainError::internal(format!("Failed to acquire read lock: {}", e))
64 })?;
65
66 context
67 .cross_domain_state
68 .get(key)
69 .cloned()
70 .ok_or_else(|| DomainError::context_error(format!("Context key not found: {}", key)))
71 }
72
73 pub fn set_project_type(&self, project_type: &str) -> DomainResult<()> {
79 let mut context = self.context.write().map_err(|e| {
80 DomainError::internal(format!("Failed to acquire write lock: {}", e))
81 })?;
82
83 context.project_type = project_type.to_string();
84 Ok(())
85 }
86
87 pub fn get_project_type(&self) -> DomainResult<String> {
89 let context = self.context.read().map_err(|e| {
90 DomainError::internal(format!("Failed to acquire read lock: {}", e))
91 })?;
92
93 Ok(context.project_type.clone())
94 }
95
96 pub fn add_technology(&self, technology: &str) -> DomainResult<()> {
102 let mut context = self.context.write().map_err(|e| {
103 DomainError::internal(format!("Failed to acquire write lock: {}", e))
104 })?;
105
106 if !context.tech_stack.contains(&technology.to_string()) {
107 context.tech_stack.push(technology.to_string());
108 }
109
110 Ok(())
111 }
112
113 pub fn get_tech_stack(&self) -> DomainResult<Vec<String>> {
115 let context = self.context.read().map_err(|e| {
116 DomainError::internal(format!("Failed to acquire read lock: {}", e))
117 })?;
118
119 Ok(context.tech_stack.clone())
120 }
121
122 pub fn add_constraint(&self, constraint: &str) -> DomainResult<()> {
128 let mut context = self.context.write().map_err(|e| {
129 DomainError::internal(format!("Failed to acquire write lock: {}", e))
130 })?;
131
132 if !context.constraints.contains(&constraint.to_string()) {
133 context.constraints.push(constraint.to_string());
134 }
135
136 Ok(())
137 }
138
139 pub fn get_constraints(&self) -> DomainResult<Vec<String>> {
141 let context = self.context.read().map_err(|e| {
142 DomainError::internal(format!("Failed to acquire read lock: {}", e))
143 })?;
144
145 Ok(context.constraints.clone())
146 }
147
148 pub fn store_agent_recommendations(
155 &self,
156 agent_id: &str,
157 recommendations: Vec<Recommendation>,
158 ) -> DomainResult<()> {
159 let mut agent_recs = self.agent_recommendations.write().map_err(|e| {
160 DomainError::internal(format!("Failed to acquire write lock: {}", e))
161 })?;
162
163 agent_recs.insert(agent_id.to_string(), recommendations);
164 Ok(())
165 }
166
167 pub fn get_agent_recommendations(&self, agent_id: &str) -> DomainResult<Vec<Recommendation>> {
177 let agent_recs = self.agent_recommendations.read().map_err(|e| {
178 DomainError::internal(format!("Failed to acquire read lock: {}", e))
179 })?;
180
181 Ok(agent_recs
182 .get(agent_id)
183 .cloned()
184 .unwrap_or_default())
185 }
186
187 pub fn get_all_recommendations(&self) -> DomainResult<Vec<Recommendation>> {
189 let agent_recs = self.agent_recommendations.read().map_err(|e| {
190 DomainError::internal(format!("Failed to acquire read lock: {}", e))
191 })?;
192
193 let mut all_recs = Vec::new();
194 for recs in agent_recs.values() {
195 all_recs.extend(recs.clone());
196 }
197
198 Ok(all_recs)
199 }
200
201 pub fn get_shared_context(&self) -> DomainResult<SharedContext> {
203 let context = self.context.read().map_err(|e| {
204 DomainError::internal(format!("Failed to acquire read lock: {}", e))
205 })?;
206
207 Ok(context.clone())
208 }
209
210 pub fn clear(&self) -> DomainResult<()> {
212 let mut context = self.context.write().map_err(|e| {
213 DomainError::internal(format!("Failed to acquire write lock: {}", e))
214 })?;
215
216 *context = SharedContext::default();
217
218 let mut agent_recs = self.agent_recommendations.write().map_err(|e| {
219 DomainError::internal(format!("Failed to acquire write lock: {}", e))
220 })?;
221
222 agent_recs.clear();
223
224 Ok(())
225 }
226}
227
228impl Default for SharedContextManager {
229 fn default() -> Self {
230 Self::new()
231 }
232}
233
234#[cfg(test)]
235mod tests {
236 use super::*;
237
238 fn create_test_recommendation(domain: &str) -> Recommendation {
239 Recommendation {
240 domain: domain.to_string(),
241 category: "test".to_string(),
242 content: "Test recommendation".to_string(),
243 technologies: vec!["Tech1".to_string()],
244 rationale: "Test rationale".to_string(),
245 }
246 }
247
248 #[test]
249 fn test_context_manager_creation() {
250 let manager = SharedContextManager::new();
251 assert!(manager.get_project_type().unwrap().is_empty());
252 }
253
254 #[test]
255 fn test_update_context() {
256 let manager = SharedContextManager::new();
257 manager
258 .update_context("key", serde_json::json!("value"))
259 .unwrap();
260
261 let value = manager.get_context("key").unwrap();
262 assert_eq!(value, serde_json::json!("value"));
263 }
264
265 #[test]
266 fn test_get_nonexistent_context() {
267 let manager = SharedContextManager::new();
268 let result = manager.get_context("nonexistent");
269
270 assert!(result.is_err());
271 }
272
273 #[test]
274 fn test_set_project_type() {
275 let manager = SharedContextManager::new();
276 manager.set_project_type("web-app").unwrap();
277
278 let project_type = manager.get_project_type().unwrap();
279 assert_eq!(project_type, "web-app");
280 }
281
282 #[test]
283 fn test_add_technology() {
284 let manager = SharedContextManager::new();
285 manager.add_technology("React").unwrap();
286
287 let tech_stack = manager.get_tech_stack().unwrap();
288 assert_eq!(tech_stack.len(), 1);
289 assert_eq!(tech_stack[0], "React");
290 }
291
292 #[test]
293 fn test_add_multiple_technologies() {
294 let manager = SharedContextManager::new();
295 manager.add_technology("React").unwrap();
296 manager.add_technology("Node.js").unwrap();
297
298 let tech_stack = manager.get_tech_stack().unwrap();
299 assert_eq!(tech_stack.len(), 2);
300 }
301
302 #[test]
303 fn test_add_duplicate_technology() {
304 let manager = SharedContextManager::new();
305 manager.add_technology("React").unwrap();
306 manager.add_technology("React").unwrap();
307
308 let tech_stack = manager.get_tech_stack().unwrap();
309 assert_eq!(tech_stack.len(), 1);
310 }
311
312 #[test]
313 fn test_add_constraint() {
314 let manager = SharedContextManager::new();
315 manager.add_constraint("Must support IE11").unwrap();
316
317 let constraints = manager.get_constraints().unwrap();
318 assert_eq!(constraints.len(), 1);
319 assert_eq!(constraints[0], "Must support IE11");
320 }
321
322 #[test]
323 fn test_add_multiple_constraints() {
324 let manager = SharedContextManager::new();
325 manager.add_constraint("Must support IE11").unwrap();
326 manager.add_constraint("Must be mobile-friendly").unwrap();
327
328 let constraints = manager.get_constraints().unwrap();
329 assert_eq!(constraints.len(), 2);
330 }
331
332 #[test]
333 fn test_store_agent_recommendations() {
334 let manager = SharedContextManager::new();
335 let recommendations = vec![create_test_recommendation("web")];
336
337 manager
338 .store_agent_recommendations("web-agent", recommendations)
339 .unwrap();
340
341 let retrieved = manager.get_agent_recommendations("web-agent").unwrap();
342 assert_eq!(retrieved.len(), 1);
343 }
344
345 #[test]
346 fn test_get_all_recommendations() {
347 let manager = SharedContextManager::new();
348
349 manager
350 .store_agent_recommendations("web-agent", vec![create_test_recommendation("web")])
351 .unwrap();
352 manager
353 .store_agent_recommendations(
354 "backend-agent",
355 vec![create_test_recommendation("backend")],
356 )
357 .unwrap();
358
359 let all_recs = manager.get_all_recommendations().unwrap();
360 assert_eq!(all_recs.len(), 2);
361 }
362
363 #[test]
364 fn test_get_shared_context() {
365 let manager = SharedContextManager::new();
366 manager.set_project_type("web-app").unwrap();
367 manager.add_technology("React").unwrap();
368
369 let context = manager.get_shared_context().unwrap();
370 assert_eq!(context.project_type, "web-app");
371 assert_eq!(context.tech_stack.len(), 1);
372 }
373
374 #[test]
375 fn test_clear() {
376 let manager = SharedContextManager::new();
377 manager.set_project_type("web-app").unwrap();
378 manager.add_technology("React").unwrap();
379 manager
380 .store_agent_recommendations("web-agent", vec![create_test_recommendation("web")])
381 .unwrap();
382
383 manager.clear().unwrap();
384
385 assert!(manager.get_project_type().unwrap().is_empty());
386 assert!(manager.get_tech_stack().unwrap().is_empty());
387 assert!(manager.get_agent_recommendations("web-agent").unwrap().is_empty());
388 }
389
390 #[test]
391 fn test_default_manager() {
392 let manager = SharedContextManager::default();
393 assert!(manager.get_project_type().unwrap().is_empty());
394 }
395
396 #[test]
397 fn test_context_isolation() {
398 let manager1 = SharedContextManager::new();
399 let manager2 = SharedContextManager::new();
400
401 manager1.set_project_type("web-app").unwrap();
402 manager2.set_project_type("mobile-app").unwrap();
403
404 assert_eq!(manager1.get_project_type().unwrap(), "web-app");
405 assert_eq!(manager2.get_project_type().unwrap(), "mobile-app");
406 }
407}