ricecoder_agents/domain/
models.rs

1//! Data models for domain-specific agents
2//!
3//! This module contains all the data structures used for domain agent communication,
4//! configuration, and result reporting.
5
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9/// A domain-specific agent
10///
11/// This struct represents a specialized agent for a specific development domain
12/// (web, backend, DevOps, etc.) with domain-specific capabilities and knowledge.
13///
14/// # Examples
15///
16/// ```ignore
17/// use ricecoder_agents::domain::DomainAgent;
18///
19/// let agent = DomainAgent {
20///     id: "web-agent".to_string(),
21///     domain: "web".to_string(),
22///     capabilities: vec![],
23///     knowledge: Default::default(),
24/// };
25/// ```
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct DomainAgent {
28    /// Unique agent identifier
29    pub id: String,
30    /// Domain name (e.g., "web", "backend", "devops")
31    pub domain: String,
32    /// Agent capabilities
33    pub capabilities: Vec<DomainCapability>,
34    /// Domain knowledge
35    pub knowledge: DomainKnowledge,
36}
37
38/// A capability of a domain agent
39///
40/// This struct represents a specific capability that a domain agent has,
41/// including the technologies it supports and patterns it knows about.
42///
43/// # Examples
44///
45/// ```ignore
46/// use ricecoder_agents::domain::DomainCapability;
47///
48/// let capability = DomainCapability {
49///     name: "Frontend Framework Selection".to_string(),
50///     description: "Recommend frontend frameworks based on project needs".to_string(),
51///     technologies: vec!["React".to_string(), "Vue".to_string(), "Angular".to_string()],
52///     patterns: vec![],
53/// };
54/// ```
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct DomainCapability {
57    /// Capability name
58    pub name: String,
59    /// Capability description
60    pub description: String,
61    /// Technologies supported by this capability
62    pub technologies: Vec<String>,
63    /// Patterns associated with this capability
64    pub patterns: Vec<Pattern>,
65}
66
67/// Domain-specific knowledge
68///
69/// This struct contains all the expertise for a specific domain,
70/// including best practices, technology recommendations, patterns, and anti-patterns.
71///
72/// # Examples
73///
74/// ```ignore
75/// use ricecoder_agents::domain::DomainKnowledge;
76///
77/// let knowledge = DomainKnowledge {
78///     best_practices: vec![],
79///     technology_recommendations: vec![],
80///     patterns: vec![],
81///     anti_patterns: vec![],
82/// };
83/// ```
84#[derive(Debug, Clone, Default, Serialize, Deserialize)]
85pub struct DomainKnowledge {
86    /// Best practices for the domain
87    pub best_practices: Vec<BestPractice>,
88    /// Technology recommendations
89    pub technology_recommendations: Vec<TechRecommendation>,
90    /// Patterns for the domain
91    pub patterns: Vec<Pattern>,
92    /// Anti-patterns to avoid
93    pub anti_patterns: Vec<AntiPattern>,
94}
95
96/// A technology recommendation
97///
98/// This struct represents a recommendation for a specific technology,
99/// including use cases, pros, cons, and alternatives.
100///
101/// # Examples
102///
103/// ```ignore
104/// use ricecoder_agents::domain::TechRecommendation;
105///
106/// let recommendation = TechRecommendation {
107///     technology: "React".to_string(),
108///     domain: "web".to_string(),
109///     use_cases: vec!["Single Page Applications".to_string()],
110///     pros: vec!["Large ecosystem".to_string()],
111///     cons: vec!["Steep learning curve".to_string()],
112///     alternatives: vec!["Vue".to_string(), "Angular".to_string()],
113/// };
114/// ```
115#[derive(Debug, Clone, Serialize, Deserialize)]
116pub struct TechRecommendation {
117    /// Technology name
118    pub technology: String,
119    /// Domain this recommendation applies to
120    pub domain: String,
121    /// Use cases for this technology
122    pub use_cases: Vec<String>,
123    /// Pros of using this technology
124    pub pros: Vec<String>,
125    /// Cons of using this technology
126    pub cons: Vec<String>,
127    /// Alternative technologies
128    pub alternatives: Vec<String>,
129}
130
131/// A best practice
132///
133/// This struct represents a best practice for a specific domain,
134/// including implementation guidance and applicable technologies.
135///
136/// # Examples
137///
138/// ```ignore
139/// use ricecoder_agents::domain::BestPractice;
140///
141/// let practice = BestPractice {
142///     title: "Component-Based Architecture".to_string(),
143///     description: "Use component-based architecture for maintainability".to_string(),
144///     domain: "web".to_string(),
145///     technologies: vec!["React".to_string(), "Vue".to_string()],
146///     implementation: "Break UI into small, reusable components".to_string(),
147/// };
148/// ```
149#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct BestPractice {
151    /// Practice title
152    pub title: String,
153    /// Practice description
154    pub description: String,
155    /// Domain this practice applies to
156    pub domain: String,
157    /// Technologies this practice applies to
158    pub technologies: Vec<String>,
159    /// Implementation guidance
160    pub implementation: String,
161}
162
163/// A pattern
164///
165/// This struct represents a design or architectural pattern,
166/// including its use cases and applicable technologies.
167///
168/// # Examples
169///
170/// ```ignore
171/// use ricecoder_agents::domain::Pattern;
172///
173/// let pattern = Pattern {
174///     name: "MVC Pattern".to_string(),
175///     description: "Model-View-Controller architectural pattern".to_string(),
176///     domain: "backend".to_string(),
177///     technologies: vec!["Django".to_string(), "Rails".to_string()],
178///     use_cases: vec!["Web applications".to_string()],
179/// };
180/// ```
181#[derive(Debug, Clone, Serialize, Deserialize)]
182pub struct Pattern {
183    /// Pattern name
184    pub name: String,
185    /// Pattern description
186    pub description: String,
187    /// Domain this pattern applies to
188    pub domain: String,
189    /// Technologies this pattern applies to
190    pub technologies: Vec<String>,
191    /// Use cases for this pattern
192    pub use_cases: Vec<String>,
193}
194
195/// An anti-pattern
196///
197/// This struct represents an anti-pattern (something to avoid),
198/// including why it should be avoided and better alternatives.
199///
200/// # Examples
201///
202/// ```ignore
203/// use ricecoder_agents::domain::AntiPattern;
204///
205/// let anti_pattern = AntiPattern {
206///     name: "God Object".to_string(),
207///     description: "A class that does too much".to_string(),
208///     domain: "backend".to_string(),
209///     why_avoid: "Violates single responsibility principle".to_string(),
210///     better_alternative: "Break into smaller, focused classes".to_string(),
211/// };
212/// ```
213#[derive(Debug, Clone, Serialize, Deserialize)]
214pub struct AntiPattern {
215    /// Anti-pattern name
216    pub name: String,
217    /// Anti-pattern description
218    pub description: String,
219    /// Domain this anti-pattern applies to
220    pub domain: String,
221    /// Why this anti-pattern should be avoided
222    pub why_avoid: String,
223    /// Better alternative to use instead
224    pub better_alternative: String,
225}
226
227/// A recommendation from a domain agent
228///
229/// This struct represents a recommendation provided by a domain agent,
230/// including the domain, category, content, and rationale.
231///
232/// # Examples
233///
234/// ```ignore
235/// use ricecoder_agents::domain::Recommendation;
236///
237/// let recommendation = Recommendation {
238///     domain: "web".to_string(),
239///     category: "framework".to_string(),
240///     content: "Use React for this project".to_string(),
241///     technologies: vec!["React".to_string()],
242///     rationale: "React is well-suited for complex UIs".to_string(),
243/// };
244/// ```
245#[derive(Debug, Clone, Serialize, Deserialize)]
246pub struct Recommendation {
247    /// Domain providing the recommendation
248    pub domain: String,
249    /// Category of the recommendation
250    pub category: String,
251    /// Recommendation content
252    pub content: String,
253    /// Technologies mentioned in the recommendation
254    pub technologies: Vec<String>,
255    /// Rationale for the recommendation
256    pub rationale: String,
257}
258
259/// Shared context across domain agents
260///
261/// This struct maintains cross-domain context that is shared between agents,
262/// including project type, tech stack, constraints, and cross-domain state.
263///
264/// # Examples
265///
266/// ```ignore
267/// use ricecoder_agents::domain::SharedContext;
268/// use std::collections::HashMap;
269///
270/// let context = SharedContext {
271///     project_type: "web-application".to_string(),
272///     tech_stack: vec!["React".to_string(), "Node.js".to_string()],
273///     constraints: vec!["Must support IE11".to_string()],
274///     cross_domain_state: HashMap::new(),
275/// };
276/// ```
277#[derive(Debug, Clone, Default, Serialize, Deserialize)]
278pub struct SharedContext {
279    /// Project type
280    pub project_type: String,
281    /// Technology stack
282    pub tech_stack: Vec<String>,
283    /// Project constraints
284    pub constraints: Vec<String>,
285    /// Cross-domain state
286    pub cross_domain_state: HashMap<String, serde_json::Value>,
287}
288
289#[cfg(test)]
290mod tests {
291    use super::*;
292
293    #[test]
294    fn test_domain_agent_creation() {
295        let agent = DomainAgent {
296            id: "web-agent".to_string(),
297            domain: "web".to_string(),
298            capabilities: vec![],
299            knowledge: DomainKnowledge::default(),
300        };
301
302        assert_eq!(agent.id, "web-agent");
303        assert_eq!(agent.domain, "web");
304        assert!(agent.capabilities.is_empty());
305    }
306
307    #[test]
308    fn test_domain_capability_creation() {
309        let capability = DomainCapability {
310            name: "Frontend Framework Selection".to_string(),
311            description: "Recommend frontend frameworks".to_string(),
312            technologies: vec!["React".to_string(), "Vue".to_string()],
313            patterns: vec![],
314        };
315
316        assert_eq!(capability.name, "Frontend Framework Selection");
317        assert_eq!(capability.technologies.len(), 2);
318    }
319
320    #[test]
321    fn test_tech_recommendation_creation() {
322        let recommendation = TechRecommendation {
323            technology: "React".to_string(),
324            domain: "web".to_string(),
325            use_cases: vec!["SPAs".to_string()],
326            pros: vec!["Large ecosystem".to_string()],
327            cons: vec!["Steep learning curve".to_string()],
328            alternatives: vec!["Vue".to_string()],
329        };
330
331        assert_eq!(recommendation.technology, "React");
332        assert_eq!(recommendation.domain, "web");
333        assert_eq!(recommendation.use_cases.len(), 1);
334        assert_eq!(recommendation.pros.len(), 1);
335        assert_eq!(recommendation.cons.len(), 1);
336        assert_eq!(recommendation.alternatives.len(), 1);
337    }
338
339    #[test]
340    fn test_best_practice_creation() {
341        let practice = BestPractice {
342            title: "Component-Based Architecture".to_string(),
343            description: "Use component-based architecture".to_string(),
344            domain: "web".to_string(),
345            technologies: vec!["React".to_string()],
346            implementation: "Break UI into components".to_string(),
347        };
348
349        assert_eq!(practice.title, "Component-Based Architecture");
350        assert_eq!(practice.domain, "web");
351    }
352
353    #[test]
354    fn test_pattern_creation() {
355        let pattern = Pattern {
356            name: "MVC Pattern".to_string(),
357            description: "Model-View-Controller pattern".to_string(),
358            domain: "backend".to_string(),
359            technologies: vec!["Django".to_string()],
360            use_cases: vec!["Web applications".to_string()],
361        };
362
363        assert_eq!(pattern.name, "MVC Pattern");
364        assert_eq!(pattern.domain, "backend");
365    }
366
367    #[test]
368    fn test_anti_pattern_creation() {
369        let anti_pattern = AntiPattern {
370            name: "God Object".to_string(),
371            description: "A class that does too much".to_string(),
372            domain: "backend".to_string(),
373            why_avoid: "Violates SRP".to_string(),
374            better_alternative: "Break into smaller classes".to_string(),
375        };
376
377        assert_eq!(anti_pattern.name, "God Object");
378        assert_eq!(anti_pattern.domain, "backend");
379    }
380
381    #[test]
382    fn test_recommendation_creation() {
383        let recommendation = Recommendation {
384            domain: "web".to_string(),
385            category: "framework".to_string(),
386            content: "Use React".to_string(),
387            technologies: vec!["React".to_string()],
388            rationale: "Well-suited for complex UIs".to_string(),
389        };
390
391        assert_eq!(recommendation.domain, "web");
392        assert_eq!(recommendation.category, "framework");
393    }
394
395    #[test]
396    fn test_shared_context_creation() {
397        let context = SharedContext {
398            project_type: "web-application".to_string(),
399            tech_stack: vec!["React".to_string(), "Node.js".to_string()],
400            constraints: vec!["Must support IE11".to_string()],
401            cross_domain_state: HashMap::new(),
402        };
403
404        assert_eq!(context.project_type, "web-application");
405        assert_eq!(context.tech_stack.len(), 2);
406        assert_eq!(context.constraints.len(), 1);
407    }
408
409    #[test]
410    fn test_domain_knowledge_default() {
411        let knowledge = DomainKnowledge::default();
412        assert!(knowledge.best_practices.is_empty());
413        assert!(knowledge.technology_recommendations.is_empty());
414        assert!(knowledge.patterns.is_empty());
415        assert!(knowledge.anti_patterns.is_empty());
416    }
417
418    #[test]
419    fn test_shared_context_default() {
420        let context = SharedContext::default();
421        assert!(context.project_type.is_empty());
422        assert!(context.tech_stack.is_empty());
423        assert!(context.constraints.is_empty());
424        assert!(context.cross_domain_state.is_empty());
425    }
426
427    #[test]
428    fn test_domain_agent_serialization() {
429        let agent = DomainAgent {
430            id: "web-agent".to_string(),
431            domain: "web".to_string(),
432            capabilities: vec![],
433            knowledge: DomainKnowledge::default(),
434        };
435
436        let json = serde_json::to_string(&agent).expect("serialization failed");
437        let deserialized: DomainAgent =
438            serde_json::from_str(&json).expect("deserialization failed");
439
440        assert_eq!(deserialized.id, agent.id);
441        assert_eq!(deserialized.domain, agent.domain);
442    }
443
444    #[test]
445    fn test_recommendation_serialization() {
446        let recommendation = Recommendation {
447            domain: "web".to_string(),
448            category: "framework".to_string(),
449            content: "Use React".to_string(),
450            technologies: vec!["React".to_string()],
451            rationale: "Well-suited".to_string(),
452        };
453
454        let json = serde_json::to_string(&recommendation).expect("serialization failed");
455        let deserialized: Recommendation =
456            serde_json::from_str(&json).expect("deserialization failed");
457
458        assert_eq!(deserialized.domain, recommendation.domain);
459        assert_eq!(deserialized.category, recommendation.category);
460    }
461
462    #[test]
463    fn test_shared_context_with_state() {
464        let mut context = SharedContext::default();
465        context
466            .cross_domain_state
467            .insert("key".to_string(), serde_json::json!("value"));
468
469        assert_eq!(context.cross_domain_state.len(), 1);
470        assert_eq!(
471            context.cross_domain_state.get("key").unwrap(),
472            &serde_json::json!("value")
473        );
474    }
475
476    #[test]
477    fn test_tech_recommendation_multiple_alternatives() {
478        let recommendation = TechRecommendation {
479            technology: "React".to_string(),
480            domain: "web".to_string(),
481            use_cases: vec!["SPAs".to_string(), "Complex UIs".to_string()],
482            pros: vec!["Ecosystem".to_string(), "Community".to_string()],
483            cons: vec!["Learning curve".to_string()],
484            alternatives: vec!["Vue".to_string(), "Angular".to_string(), "Svelte".to_string()],
485        };
486
487        assert_eq!(recommendation.use_cases.len(), 2);
488        assert_eq!(recommendation.pros.len(), 2);
489        assert_eq!(recommendation.alternatives.len(), 3);
490    }
491
492    #[test]
493    fn test_domain_capability_with_patterns() {
494        let patterns = vec![Pattern {
495            name: "Component Pattern".to_string(),
496            description: "Component-based architecture".to_string(),
497            domain: "web".to_string(),
498            technologies: vec!["React".to_string()],
499            use_cases: vec!["UI development".to_string()],
500        }];
501
502        let capability = DomainCapability {
503            name: "Frontend Framework".to_string(),
504            description: "Frontend framework selection".to_string(),
505            technologies: vec!["React".to_string()],
506            patterns,
507        };
508
509        assert_eq!(capability.patterns.len(), 1);
510        assert_eq!(capability.patterns[0].name, "Component Pattern");
511    }
512
513    #[test]
514    fn test_domain_knowledge_with_all_fields() {
515        let knowledge = DomainKnowledge {
516            best_practices: vec![BestPractice {
517                title: "Practice".to_string(),
518                description: "Description".to_string(),
519                domain: "web".to_string(),
520                technologies: vec!["React".to_string()],
521                implementation: "Implementation".to_string(),
522            }],
523            technology_recommendations: vec![TechRecommendation {
524                technology: "React".to_string(),
525                domain: "web".to_string(),
526                use_cases: vec!["SPAs".to_string()],
527                pros: vec!["Ecosystem".to_string()],
528                cons: vec!["Learning curve".to_string()],
529                alternatives: vec!["Vue".to_string()],
530            }],
531            patterns: vec![Pattern {
532                name: "Pattern".to_string(),
533                description: "Description".to_string(),
534                domain: "web".to_string(),
535                technologies: vec!["React".to_string()],
536                use_cases: vec!["UI".to_string()],
537            }],
538            anti_patterns: vec![AntiPattern {
539                name: "Anti-pattern".to_string(),
540                description: "Description".to_string(),
541                domain: "web".to_string(),
542                why_avoid: "Reason".to_string(),
543                better_alternative: "Alternative".to_string(),
544            }],
545        };
546
547        assert_eq!(knowledge.best_practices.len(), 1);
548        assert_eq!(knowledge.technology_recommendations.len(), 1);
549        assert_eq!(knowledge.patterns.len(), 1);
550        assert_eq!(knowledge.anti_patterns.len(), 1);
551    }
552}