1use crate::domain::error::{DomainError, DomainResult};
4use crate::domain::models::{
5 AntiPattern, BestPractice, DomainAgent, DomainCapability, DomainKnowledge, Pattern,
6 TechRecommendation,
7};
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct AgentConfig {
16 pub domain: String,
18 pub name: String,
20 pub description: String,
22 pub capabilities: Vec<CapabilityConfig>,
24 #[serde(default)]
26 pub best_practices: Vec<BestPracticeConfig>,
27 #[serde(default)]
29 pub technology_recommendations: Vec<TechRecommendationConfig>,
30 #[serde(default)]
32 pub patterns: Vec<PatternConfig>,
33 #[serde(default)]
35 pub anti_patterns: Vec<AntiPatternConfig>,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct CapabilityConfig {
41 pub name: String,
43 pub description: String,
45 pub technologies: Vec<String>,
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct BestPracticeConfig {
52 pub title: String,
54 pub description: String,
56 pub technologies: Vec<String>,
58 pub implementation: String,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct TechRecommendationConfig {
65 pub technology: String,
67 pub use_cases: Vec<String>,
69 pub pros: Vec<String>,
71 pub cons: Vec<String>,
73 pub alternatives: Vec<String>,
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct PatternConfig {
80 pub name: String,
82 pub description: String,
84 pub technologies: Vec<String>,
86 pub use_cases: Vec<String>,
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize)]
92pub struct AntiPatternConfig {
93 pub name: String,
95 pub description: String,
97 pub why_avoid: String,
99 pub better_alternative: String,
101}
102
103#[derive(Debug, Clone)]
118pub struct AgentFactory;
119
120impl AgentFactory {
121 pub fn new() -> Self {
123 Self
124 }
125
126 pub fn create_agent(&self, domain: &str, config: &AgentConfig) -> DomainResult<DomainAgent> {
143 self.validate_config(config)?;
145
146 let capabilities = config
148 .capabilities
149 .iter()
150 .map(|cap| DomainCapability {
151 name: cap.name.clone(),
152 description: cap.description.clone(),
153 technologies: cap.technologies.clone(),
154 patterns: vec![],
155 })
156 .collect();
157
158 let best_practices = config
160 .best_practices
161 .iter()
162 .map(|bp| BestPractice {
163 title: bp.title.clone(),
164 description: bp.description.clone(),
165 domain: domain.to_string(),
166 technologies: bp.technologies.clone(),
167 implementation: bp.implementation.clone(),
168 })
169 .collect();
170
171 let technology_recommendations = config
173 .technology_recommendations
174 .iter()
175 .map(|tr| TechRecommendation {
176 technology: tr.technology.clone(),
177 domain: domain.to_string(),
178 use_cases: tr.use_cases.clone(),
179 pros: tr.pros.clone(),
180 cons: tr.cons.clone(),
181 alternatives: tr.alternatives.clone(),
182 })
183 .collect();
184
185 let patterns = config
187 .patterns
188 .iter()
189 .map(|p| Pattern {
190 name: p.name.clone(),
191 description: p.description.clone(),
192 domain: domain.to_string(),
193 technologies: p.technologies.clone(),
194 use_cases: p.use_cases.clone(),
195 })
196 .collect();
197
198 let anti_patterns = config
200 .anti_patterns
201 .iter()
202 .map(|ap| AntiPattern {
203 name: ap.name.clone(),
204 description: ap.description.clone(),
205 domain: domain.to_string(),
206 why_avoid: ap.why_avoid.clone(),
207 better_alternative: ap.better_alternative.clone(),
208 })
209 .collect();
210
211 let knowledge = DomainKnowledge {
213 best_practices,
214 technology_recommendations,
215 patterns,
216 anti_patterns,
217 };
218
219 let agent = DomainAgent {
221 id: format!("{}-agent", domain),
222 domain: domain.to_string(),
223 capabilities,
224 knowledge,
225 };
226
227 Ok(agent)
228 }
229
230 pub fn validate_config(&self, config: &AgentConfig) -> DomainResult<()> {
240 if config.domain.is_empty() {
242 return Err(DomainError::config_error("Domain cannot be empty"));
243 }
244
245 if config.name.is_empty() {
247 return Err(DomainError::config_error("Agent name cannot be empty"));
248 }
249
250 if config.capabilities.is_empty() {
252 return Err(DomainError::config_error("At least one capability is required"));
253 }
254
255 for cap in &config.capabilities {
257 if cap.name.is_empty() {
258 return Err(DomainError::config_error("Capability name cannot be empty"));
259 }
260 if cap.technologies.is_empty() {
261 return Err(DomainError::config_error(
262 "Capability must have at least one technology",
263 ));
264 }
265 }
266
267 Ok(())
268 }
269
270 pub fn load_from_json(&self, json: &str) -> DomainResult<AgentConfig> {
280 serde_json::from_str(json).map_err(|e| {
281 DomainError::serialization_error(format!("Failed to parse JSON: {}", e))
282 })
283 }
284
285 pub fn load_from_yaml(&self, _yaml: &str) -> DomainResult<AgentConfig> {
297 Err(DomainError::config_error(
298 "YAML support requires serde_yaml dependency",
299 ))
300 }
301}
302
303impl Default for AgentFactory {
304 fn default() -> Self {
305 Self::new()
306 }
307}
308
309#[cfg(test)]
310mod tests {
311 use super::*;
312
313 fn create_test_config(domain: &str) -> AgentConfig {
314 AgentConfig {
315 domain: domain.to_string(),
316 name: format!("{} Agent", domain),
317 description: format!("Agent for {} development", domain),
318 capabilities: vec![CapabilityConfig {
319 name: "Test Capability".to_string(),
320 description: "A test capability".to_string(),
321 technologies: vec!["Tech1".to_string()],
322 }],
323 best_practices: vec![],
324 technology_recommendations: vec![],
325 patterns: vec![],
326 anti_patterns: vec![],
327 }
328 }
329
330 #[test]
331 fn test_factory_creation() {
332 let factory = AgentFactory::new();
333 assert_eq!(std::mem::size_of_val(&factory), 0); }
335
336 #[test]
337 fn test_create_agent() {
338 let factory = AgentFactory::new();
339 let config = create_test_config("web");
340
341 let agent = factory.create_agent("web", &config).unwrap();
342
343 assert_eq!(agent.domain, "web");
344 assert_eq!(agent.id, "web-agent");
345 assert_eq!(agent.capabilities.len(), 1);
346 }
347
348 #[test]
349 fn test_validate_config_valid() {
350 let factory = AgentFactory::new();
351 let config = create_test_config("web");
352
353 assert!(factory.validate_config(&config).is_ok());
354 }
355
356 #[test]
357 fn test_validate_config_empty_domain() {
358 let factory = AgentFactory::new();
359 let mut config = create_test_config("web");
360 config.domain = String::new();
361
362 assert!(factory.validate_config(&config).is_err());
363 }
364
365 #[test]
366 fn test_validate_config_empty_name() {
367 let factory = AgentFactory::new();
368 let mut config = create_test_config("web");
369 config.name = String::new();
370
371 assert!(factory.validate_config(&config).is_err());
372 }
373
374 #[test]
375 fn test_validate_config_no_capabilities() {
376 let factory = AgentFactory::new();
377 let mut config = create_test_config("web");
378 config.capabilities = vec![];
379
380 assert!(factory.validate_config(&config).is_err());
381 }
382
383 #[test]
384 fn test_validate_config_empty_capability_name() {
385 let factory = AgentFactory::new();
386 let mut config = create_test_config("web");
387 config.capabilities[0].name = String::new();
388
389 assert!(factory.validate_config(&config).is_err());
390 }
391
392 #[test]
393 fn test_validate_config_no_technologies() {
394 let factory = AgentFactory::new();
395 let mut config = create_test_config("web");
396 config.capabilities[0].technologies = vec![];
397
398 assert!(factory.validate_config(&config).is_err());
399 }
400
401 #[test]
402 fn test_create_agent_with_best_practices() {
403 let factory = AgentFactory::new();
404 let mut config = create_test_config("web");
405 config.best_practices = vec![BestPracticeConfig {
406 title: "Practice 1".to_string(),
407 description: "Description".to_string(),
408 technologies: vec!["React".to_string()],
409 implementation: "Implementation".to_string(),
410 }];
411
412 let agent = factory.create_agent("web", &config).unwrap();
413
414 assert_eq!(agent.knowledge.best_practices.len(), 1);
415 assert_eq!(agent.knowledge.best_practices[0].title, "Practice 1");
416 }
417
418 #[test]
419 fn test_create_agent_with_tech_recommendations() {
420 let factory = AgentFactory::new();
421 let mut config = create_test_config("web");
422 config.technology_recommendations = vec![TechRecommendationConfig {
423 technology: "React".to_string(),
424 use_cases: vec!["SPAs".to_string()],
425 pros: vec!["Ecosystem".to_string()],
426 cons: vec!["Learning curve".to_string()],
427 alternatives: vec!["Vue".to_string()],
428 }];
429
430 let agent = factory.create_agent("web", &config).unwrap();
431
432 assert_eq!(agent.knowledge.technology_recommendations.len(), 1);
433 assert_eq!(agent.knowledge.technology_recommendations[0].technology, "React");
434 }
435
436 #[test]
437 fn test_create_agent_with_patterns() {
438 let factory = AgentFactory::new();
439 let mut config = create_test_config("web");
440 config.patterns = vec![PatternConfig {
441 name: "Pattern 1".to_string(),
442 description: "Description".to_string(),
443 technologies: vec!["React".to_string()],
444 use_cases: vec!["UI".to_string()],
445 }];
446
447 let agent = factory.create_agent("web", &config).unwrap();
448
449 assert_eq!(agent.knowledge.patterns.len(), 1);
450 assert_eq!(agent.knowledge.patterns[0].name, "Pattern 1");
451 }
452
453 #[test]
454 fn test_create_agent_with_anti_patterns() {
455 let factory = AgentFactory::new();
456 let mut config = create_test_config("web");
457 config.anti_patterns = vec![AntiPatternConfig {
458 name: "Anti-pattern 1".to_string(),
459 description: "Description".to_string(),
460 why_avoid: "Reason".to_string(),
461 better_alternative: "Alternative".to_string(),
462 }];
463
464 let agent = factory.create_agent("web", &config).unwrap();
465
466 assert_eq!(agent.knowledge.anti_patterns.len(), 1);
467 assert_eq!(agent.knowledge.anti_patterns[0].name, "Anti-pattern 1");
468 }
469
470 #[test]
471 fn test_load_from_json() {
472 let factory = AgentFactory::new();
473 let json = r#"{
474 "domain": "web",
475 "name": "Web Agent",
476 "description": "Web development agent",
477 "capabilities": [
478 {
479 "name": "Framework Selection",
480 "description": "Select frameworks",
481 "technologies": ["React"]
482 }
483 ],
484 "best_practices": [],
485 "technology_recommendations": [],
486 "patterns": [],
487 "anti_patterns": []
488 }"#;
489
490 let config = factory.load_from_json(json).unwrap();
491 assert_eq!(config.domain, "web");
492 assert_eq!(config.name, "Web Agent");
493 }
494
495 #[test]
496 fn test_load_from_json_invalid() {
497 let factory = AgentFactory::new();
498 let json = "invalid json";
499
500 assert!(factory.load_from_json(json).is_err());
501 }
502
503 #[test]
504 fn test_default_factory() {
505 let factory = AgentFactory::default();
506 let config = create_test_config("web");
507
508 assert!(factory.create_agent("web", &config).is_ok());
509 }
510
511 #[test]
512 fn test_create_agent_multiple_capabilities() {
513 let factory = AgentFactory::new();
514 let mut config = create_test_config("web");
515 config.capabilities = vec![
516 CapabilityConfig {
517 name: "Capability 1".to_string(),
518 description: "Description 1".to_string(),
519 technologies: vec!["Tech1".to_string()],
520 },
521 CapabilityConfig {
522 name: "Capability 2".to_string(),
523 description: "Description 2".to_string(),
524 technologies: vec!["Tech2".to_string()],
525 },
526 ];
527
528 let agent = factory.create_agent("web", &config).unwrap();
529
530 assert_eq!(agent.capabilities.len(), 2);
531 }
532
533 #[test]
534 fn test_create_agent_preserves_domain() {
535 let factory = AgentFactory::new();
536 let mut config = create_test_config("backend");
537 config.best_practices = vec![BestPracticeConfig {
538 title: "Practice 1".to_string(),
539 description: "Description".to_string(),
540 technologies: vec!["Tech1".to_string()],
541 implementation: "Implementation".to_string(),
542 }];
543
544 let agent = factory.create_agent("backend", &config).unwrap();
545
546 assert_eq!(agent.domain, "backend");
547 assert_eq!(agent.knowledge.best_practices[0].domain, "backend");
548 }
549}