1use crate::domain::error::{DomainError, DomainResult};
4use crate::domain::models::{BestPractice, Pattern, TechRecommendation, AntiPattern};
5use std::collections::HashMap;
6use std::sync::{Arc, RwLock};
7
8#[derive(Debug, Clone)]
22pub struct KnowledgeBase {
23 best_practices: Arc<RwLock<HashMap<String, Vec<BestPractice>>>>,
24 tech_recommendations: Arc<RwLock<HashMap<String, Vec<TechRecommendation>>>>,
25 patterns: Arc<RwLock<HashMap<String, Vec<Pattern>>>>,
26 anti_patterns: Arc<RwLock<HashMap<String, Vec<AntiPattern>>>>,
27}
28
29impl KnowledgeBase {
30 pub fn new() -> Self {
32 Self {
33 best_practices: Arc::new(RwLock::new(HashMap::new())),
34 tech_recommendations: Arc::new(RwLock::new(HashMap::new())),
35 patterns: Arc::new(RwLock::new(HashMap::new())),
36 anti_patterns: Arc::new(RwLock::new(HashMap::new())),
37 }
38 }
39
40 pub fn add_best_practice(&self, domain: &str, practice: BestPractice) -> DomainResult<()> {
47 let mut practices = self.best_practices.write().map_err(|e| {
48 DomainError::internal(format!("Failed to acquire write lock: {}", e))
49 })?;
50
51 practices
52 .entry(domain.to_string())
53 .or_insert_with(Vec::new)
54 .push(practice);
55
56 Ok(())
57 }
58
59 pub fn get_best_practices(&self, domain: &str) -> DomainResult<Vec<BestPractice>> {
69 let practices = self.best_practices.read().map_err(|e| {
70 DomainError::internal(format!("Failed to acquire read lock: {}", e))
71 })?;
72
73 Ok(practices
74 .get(domain)
75 .cloned()
76 .unwrap_or_default())
77 }
78
79 pub fn add_tech_recommendation(
86 &self,
87 domain: &str,
88 recommendation: TechRecommendation,
89 ) -> DomainResult<()> {
90 let mut recommendations = self.tech_recommendations.write().map_err(|e| {
91 DomainError::internal(format!("Failed to acquire write lock: {}", e))
92 })?;
93
94 recommendations
95 .entry(domain.to_string())
96 .or_insert_with(Vec::new)
97 .push(recommendation);
98
99 Ok(())
100 }
101
102 pub fn get_tech_recommendations(&self, domain: &str) -> DomainResult<Vec<TechRecommendation>> {
112 let recommendations = self.tech_recommendations.read().map_err(|e| {
113 DomainError::internal(format!("Failed to acquire read lock: {}", e))
114 })?;
115
116 Ok(recommendations
117 .get(domain)
118 .cloned()
119 .unwrap_or_default())
120 }
121
122 pub fn get_tech_recommendation(
133 &self,
134 domain: &str,
135 technology: &str,
136 ) -> DomainResult<TechRecommendation> {
137 let recommendations = self.get_tech_recommendations(domain)?;
138
139 recommendations
140 .into_iter()
141 .find(|r| r.technology == technology)
142 .ok_or_else(|| DomainError::knowledge_not_found(technology))
143 }
144
145 pub fn add_pattern(&self, domain: &str, pattern: Pattern) -> DomainResult<()> {
152 let mut patterns = self.patterns.write().map_err(|e| {
153 DomainError::internal(format!("Failed to acquire write lock: {}", e))
154 })?;
155
156 patterns
157 .entry(domain.to_string())
158 .or_insert_with(Vec::new)
159 .push(pattern);
160
161 Ok(())
162 }
163
164 pub fn get_patterns(&self, domain: &str) -> DomainResult<Vec<Pattern>> {
174 let patterns = self.patterns.read().map_err(|e| {
175 DomainError::internal(format!("Failed to acquire read lock: {}", e))
176 })?;
177
178 Ok(patterns
179 .get(domain)
180 .cloned()
181 .unwrap_or_default())
182 }
183
184 pub fn add_anti_pattern(&self, domain: &str, anti_pattern: AntiPattern) -> DomainResult<()> {
191 let mut anti_patterns = self.anti_patterns.write().map_err(|e| {
192 DomainError::internal(format!("Failed to acquire write lock: {}", e))
193 })?;
194
195 anti_patterns
196 .entry(domain.to_string())
197 .or_insert_with(Vec::new)
198 .push(anti_pattern);
199
200 Ok(())
201 }
202
203 pub fn get_anti_patterns(&self, domain: &str) -> DomainResult<Vec<AntiPattern>> {
213 let anti_patterns = self.anti_patterns.read().map_err(|e| {
214 DomainError::internal(format!("Failed to acquire read lock: {}", e))
215 })?;
216
217 Ok(anti_patterns
218 .get(domain)
219 .cloned()
220 .unwrap_or_default())
221 }
222
223 pub fn clear_domain(&self, domain: &str) -> DomainResult<()> {
229 let mut practices = self.best_practices.write().map_err(|e| {
230 DomainError::internal(format!("Failed to acquire write lock: {}", e))
231 })?;
232 practices.remove(domain);
233
234 let mut recommendations = self.tech_recommendations.write().map_err(|e| {
235 DomainError::internal(format!("Failed to acquire write lock: {}", e))
236 })?;
237 recommendations.remove(domain);
238
239 let mut patterns = self.patterns.write().map_err(|e| {
240 DomainError::internal(format!("Failed to acquire write lock: {}", e))
241 })?;
242 patterns.remove(domain);
243
244 let mut anti_patterns = self.anti_patterns.write().map_err(|e| {
245 DomainError::internal(format!("Failed to acquire write lock: {}", e))
246 })?;
247 anti_patterns.remove(domain);
248
249 Ok(())
250 }
251
252 pub fn clear_all(&self) -> DomainResult<()> {
254 let mut practices = self.best_practices.write().map_err(|e| {
255 DomainError::internal(format!("Failed to acquire write lock: {}", e))
256 })?;
257 practices.clear();
258
259 let mut recommendations = self.tech_recommendations.write().map_err(|e| {
260 DomainError::internal(format!("Failed to acquire write lock: {}", e))
261 })?;
262 recommendations.clear();
263
264 let mut patterns = self.patterns.write().map_err(|e| {
265 DomainError::internal(format!("Failed to acquire write lock: {}", e))
266 })?;
267 patterns.clear();
268
269 let mut anti_patterns = self.anti_patterns.write().map_err(|e| {
270 DomainError::internal(format!("Failed to acquire write lock: {}", e))
271 })?;
272 anti_patterns.clear();
273
274 Ok(())
275 }
276}
277
278impl Default for KnowledgeBase {
279 fn default() -> Self {
280 Self::new()
281 }
282}
283
284#[cfg(test)]
285mod tests {
286 use super::*;
287
288 fn create_test_practice(domain: &str) -> BestPractice {
289 BestPractice {
290 title: "Test Practice".to_string(),
291 description: "A test practice".to_string(),
292 domain: domain.to_string(),
293 technologies: vec!["Tech1".to_string()],
294 implementation: "Implementation".to_string(),
295 }
296 }
297
298 fn create_test_recommendation(domain: &str) -> TechRecommendation {
299 TechRecommendation {
300 technology: "React".to_string(),
301 domain: domain.to_string(),
302 use_cases: vec!["SPAs".to_string()],
303 pros: vec!["Ecosystem".to_string()],
304 cons: vec!["Learning curve".to_string()],
305 alternatives: vec!["Vue".to_string()],
306 }
307 }
308
309 fn create_test_pattern(domain: &str) -> Pattern {
310 Pattern {
311 name: "Test Pattern".to_string(),
312 description: "A test pattern".to_string(),
313 domain: domain.to_string(),
314 technologies: vec!["Tech1".to_string()],
315 use_cases: vec!["Use case 1".to_string()],
316 }
317 }
318
319 fn create_test_anti_pattern(domain: &str) -> AntiPattern {
320 AntiPattern {
321 name: "Test Anti-pattern".to_string(),
322 description: "A test anti-pattern".to_string(),
323 domain: domain.to_string(),
324 why_avoid: "Reason".to_string(),
325 better_alternative: "Alternative".to_string(),
326 }
327 }
328
329 #[test]
330 fn test_knowledge_base_creation() {
331 let kb = KnowledgeBase::new();
332 assert!(kb.get_best_practices("web").unwrap().is_empty());
333 }
334
335 #[test]
336 fn test_add_best_practice() {
337 let kb = KnowledgeBase::new();
338 let practice = create_test_practice("web");
339
340 kb.add_best_practice("web", practice).unwrap();
341 let practices = kb.get_best_practices("web").unwrap();
342
343 assert_eq!(practices.len(), 1);
344 assert_eq!(practices[0].title, "Test Practice");
345 }
346
347 #[test]
348 fn test_add_tech_recommendation() {
349 let kb = KnowledgeBase::new();
350 let recommendation = create_test_recommendation("web");
351
352 kb.add_tech_recommendation("web", recommendation).unwrap();
353 let recommendations = kb.get_tech_recommendations("web").unwrap();
354
355 assert_eq!(recommendations.len(), 1);
356 assert_eq!(recommendations[0].technology, "React");
357 }
358
359 #[test]
360 fn test_get_tech_recommendation() {
361 let kb = KnowledgeBase::new();
362 let recommendation = create_test_recommendation("web");
363
364 kb.add_tech_recommendation("web", recommendation).unwrap();
365 let retrieved = kb.get_tech_recommendation("web", "React").unwrap();
366
367 assert_eq!(retrieved.technology, "React");
368 }
369
370 #[test]
371 fn test_get_nonexistent_tech_recommendation() {
372 let kb = KnowledgeBase::new();
373 let result = kb.get_tech_recommendation("web", "NonExistent");
374
375 assert!(result.is_err());
376 }
377
378 #[test]
379 fn test_add_pattern() {
380 let kb = KnowledgeBase::new();
381 let pattern = create_test_pattern("web");
382
383 kb.add_pattern("web", pattern).unwrap();
384 let patterns = kb.get_patterns("web").unwrap();
385
386 assert_eq!(patterns.len(), 1);
387 assert_eq!(patterns[0].name, "Test Pattern");
388 }
389
390 #[test]
391 fn test_add_anti_pattern() {
392 let kb = KnowledgeBase::new();
393 let anti_pattern = create_test_anti_pattern("web");
394
395 kb.add_anti_pattern("web", anti_pattern).unwrap();
396 let anti_patterns = kb.get_anti_patterns("web").unwrap();
397
398 assert_eq!(anti_patterns.len(), 1);
399 assert_eq!(anti_patterns[0].name, "Test Anti-pattern");
400 }
401
402 #[test]
403 fn test_clear_domain() {
404 let kb = KnowledgeBase::new();
405
406 kb.add_best_practice("web", create_test_practice("web")).unwrap();
407 kb.add_tech_recommendation("web", create_test_recommendation("web")).unwrap();
408
409 kb.clear_domain("web").unwrap();
410
411 assert!(kb.get_best_practices("web").unwrap().is_empty());
412 assert!(kb.get_tech_recommendations("web").unwrap().is_empty());
413 }
414
415 #[test]
416 fn test_clear_all() {
417 let kb = KnowledgeBase::new();
418
419 kb.add_best_practice("web", create_test_practice("web")).unwrap();
420 kb.add_best_practice("backend", create_test_practice("backend")).unwrap();
421
422 kb.clear_all().unwrap();
423
424 assert!(kb.get_best_practices("web").unwrap().is_empty());
425 assert!(kb.get_best_practices("backend").unwrap().is_empty());
426 }
427
428 #[test]
429 fn test_multiple_domains() {
430 let kb = KnowledgeBase::new();
431
432 kb.add_best_practice("web", create_test_practice("web")).unwrap();
433 kb.add_best_practice("backend", create_test_practice("backend")).unwrap();
434
435 let web_practices = kb.get_best_practices("web").unwrap();
436 let backend_practices = kb.get_best_practices("backend").unwrap();
437
438 assert_eq!(web_practices.len(), 1);
439 assert_eq!(backend_practices.len(), 1);
440 }
441
442 #[test]
443 fn test_default_knowledge_base() {
444 let kb = KnowledgeBase::default();
445 assert!(kb.get_best_practices("web").unwrap().is_empty());
446 }
447
448 #[test]
449 fn test_multiple_practices_per_domain() {
450 let kb = KnowledgeBase::new();
451
452 kb.add_best_practice("web", create_test_practice("web")).unwrap();
453 let mut practice2 = create_test_practice("web");
454 practice2.title = "Practice 2".to_string();
455 kb.add_best_practice("web", practice2).unwrap();
456
457 let practices = kb.get_best_practices("web").unwrap();
458 assert_eq!(practices.len(), 2);
459 }
460
461 #[test]
462 fn test_multiple_recommendations_per_domain() {
463 let kb = KnowledgeBase::new();
464
465 kb.add_tech_recommendation("web", create_test_recommendation("web")).unwrap();
466 let mut rec2 = create_test_recommendation("web");
467 rec2.technology = "Vue".to_string();
468 kb.add_tech_recommendation("web", rec2).unwrap();
469
470 let recommendations = kb.get_tech_recommendations("web").unwrap();
471 assert_eq!(recommendations.len(), 2);
472 }
473}