Skip to main content

driven/context/
provider.rs

1//! Context provider for AI agents
2
3use super::{ProjectContext, ProjectScanner};
4use crate::{Result, parser::UnifiedRule};
5use std::path::Path;
6
7/// Provides context to AI agents
8#[derive(Debug, Default)]
9pub struct ContextProvider {
10    context: Option<ProjectContext>,
11}
12
13impl ContextProvider {
14    /// Create a new context provider
15    pub fn new() -> Self {
16        Self { context: None }
17    }
18
19    /// Create with pre-loaded context
20    pub fn with_context(context: ProjectContext) -> Self {
21        Self {
22            context: Some(context),
23        }
24    }
25
26    /// Set the project context
27    pub fn set_context(&mut self, context: ProjectContext) {
28        self.context = Some(context);
29    }
30
31    /// Get the current context
32    pub fn get_context(&self) -> Option<&ProjectContext> {
33        self.context.as_ref()
34    }
35
36    /// Generate context from a project path
37    pub fn generate(&self, project_root: &Path) -> Result<String> {
38        let scanner = ProjectScanner::new();
39        let result = scanner.scan(project_root)?;
40
41        let mut output = String::new();
42        output.push_str("# Project Context\n\n");
43
44        output.push_str("## Languages\n");
45        for lang in &result.languages {
46            output.push_str(&format!("- {}\n", lang));
47        }
48
49        output.push_str("\n## Frameworks\n");
50        for framework in &result.frameworks {
51            output.push_str(&format!("- {}\n", framework));
52        }
53
54        if !result.key_directories.is_empty() {
55            output.push_str("\n## Key Directories\n");
56            for dir in &result.key_directories {
57                output.push_str(&format!("- {}\n", dir));
58            }
59        }
60
61        Ok(output)
62    }
63
64    /// Generate context rules for AI agents
65    pub fn generate_rules(&self) -> Result<Vec<UnifiedRule>> {
66        let Some(context) = &self.context else {
67            return Ok(Vec::new());
68        };
69
70        let mut rules = Vec::new();
71
72        // Add project type context
73        let mut focus = Vec::new();
74
75        if let Some(project_type) = &context.project_type {
76            focus.push(format!("This is a {} project", project_type));
77        }
78
79        if !context.languages.is_empty() {
80            focus.push(format!("Languages: {}", context.languages.join(", ")));
81        }
82
83        if !context.frameworks.is_empty() {
84            focus.push(format!("Frameworks: {}", context.frameworks.join(", ")));
85        }
86
87        // Add naming conventions
88        if let Some(style) = &context.naming_conventions.functions {
89            focus.push(format!("Use {} for functions", style));
90        }
91
92        if let Some(style) = &context.naming_conventions.types {
93            focus.push(format!("Use {} for types", style));
94        }
95
96        // Add patterns
97        focus.extend(context.patterns.clone());
98
99        if !focus.is_empty() {
100            rules.push(UnifiedRule::Context {
101                includes: context.directories.clone(),
102                excludes: Vec::new(),
103                focus,
104            });
105        }
106
107        Ok(rules)
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114
115    #[test]
116    fn test_provider_new() {
117        let provider = ContextProvider::new();
118        assert!(provider.get_context().is_none());
119    }
120
121    #[test]
122    fn test_provider_with_context() {
123        let context = ProjectContext {
124            project_type: Some("Rust Project".to_string()),
125            ..Default::default()
126        };
127
128        let provider = ContextProvider::with_context(context);
129        assert!(provider.get_context().is_some());
130    }
131
132    #[test]
133    fn test_generate_rules_empty() {
134        let provider = ContextProvider::new();
135        let rules = provider.generate_rules().unwrap();
136        assert!(rules.is_empty());
137    }
138
139    #[test]
140    fn test_generate_rules_with_context() {
141        let context = ProjectContext {
142            project_type: Some("Rust Project".to_string()),
143            languages: vec!["Rust".to_string()],
144            ..Default::default()
145        };
146
147        let provider = ContextProvider::with_context(context);
148        let rules = provider.generate_rules().unwrap();
149        assert!(!rules.is_empty());
150    }
151}