Skip to main content

driven/context/
mod.rs

1//! AI Context Intelligence
2//!
3//! Deep project analysis for AI guidance.
4
5mod analyzer;
6mod extractor;
7mod indexer;
8mod provider;
9mod scanner;
10
11pub use analyzer::PatternAnalyzer;
12pub use extractor::ConventionExtractor;
13pub use indexer::CodebaseIndexer;
14pub use provider::ContextProvider;
15pub use scanner::ProjectScanner;
16
17use crate::Result;
18use std::path::Path;
19
20/// Analyzed project context
21#[derive(Debug, Clone, Default)]
22pub struct ProjectContext {
23    /// Detected programming languages
24    pub languages: Vec<String>,
25    /// Detected frameworks
26    pub frameworks: Vec<String>,
27    /// Project type (library, binary, webapp, etc.)
28    pub project_type: Option<String>,
29    /// Key directories
30    pub directories: Vec<String>,
31    /// Configuration files found
32    pub config_files: Vec<String>,
33    /// Detected naming conventions
34    pub naming_conventions: NamingConventions,
35    /// Dependencies (name, version)
36    pub dependencies: Vec<(String, String)>,
37    /// Key patterns detected
38    pub patterns: Vec<String>,
39}
40
41/// Naming convention detection
42#[derive(Debug, Clone, Default)]
43pub struct NamingConventions {
44    /// Function naming style
45    pub functions: Option<NamingStyle>,
46    /// Type naming style
47    pub types: Option<NamingStyle>,
48    /// Variable naming style
49    pub variables: Option<NamingStyle>,
50    /// File naming style
51    pub files: Option<NamingStyle>,
52}
53
54/// Naming style types
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub enum NamingStyle {
57    /// snake_case
58    SnakeCase,
59    /// camelCase
60    CamelCase,
61    /// PascalCase
62    PascalCase,
63    /// kebab-case
64    KebabCase,
65    /// SCREAMING_SNAKE_CASE
66    ScreamingSnakeCase,
67}
68
69impl std::fmt::Display for NamingStyle {
70    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71        match self {
72            NamingStyle::SnakeCase => write!(f, "snake_case"),
73            NamingStyle::CamelCase => write!(f, "camelCase"),
74            NamingStyle::PascalCase => write!(f, "PascalCase"),
75            NamingStyle::KebabCase => write!(f, "kebab-case"),
76            NamingStyle::ScreamingSnakeCase => write!(f, "SCREAMING_SNAKE_CASE"),
77        }
78    }
79}
80
81/// Analyze a project and return context
82pub struct ProjectAnalyzer {
83    scanner: ProjectScanner,
84    extractor: ConventionExtractor,
85}
86
87impl ProjectAnalyzer {
88    /// Create a new project analyzer
89    pub fn new() -> Self {
90        Self {
91            scanner: ProjectScanner::new(),
92            extractor: ConventionExtractor::new(),
93        }
94    }
95
96    /// Analyze a project at the given path
97    pub fn analyze(&self, path: &Path) -> Result<ProjectContext> {
98        // Scan the project structure
99        let scan_result = self.scanner.scan(path)?;
100
101        // Extract conventions
102        let conventions = self.extractor.extract(&scan_result)?;
103
104        // Build context
105        let mut context = ProjectContext {
106            languages: scan_result.languages.clone(),
107            frameworks: scan_result.frameworks.clone(),
108            project_type: scan_result.project_type.clone(),
109            directories: scan_result.key_directories.clone(),
110            config_files: scan_result.config_files.clone(),
111            naming_conventions: conventions,
112            ..Default::default()
113        };
114
115        // Detect patterns
116        context.patterns = self.detect_patterns(&scan_result);
117
118        Ok(context)
119    }
120
121    fn detect_patterns(&self, scan_result: &scanner::ScanResult) -> Vec<String> {
122        let mut patterns = Vec::new();
123
124        if scan_result.has_tests {
125            patterns.push("Has test suite".to_string());
126        }
127
128        if scan_result.has_ci {
129            patterns.push("Has CI/CD configuration".to_string());
130        }
131
132        if scan_result.has_docs {
133            patterns.push("Has documentation".to_string());
134        }
135
136        patterns
137    }
138}
139
140impl Default for ProjectAnalyzer {
141    fn default() -> Self {
142        Self::new()
143    }
144}
145
146#[cfg(test)]
147mod tests {
148    use super::*;
149
150    #[test]
151    fn test_naming_style_display() {
152        assert_eq!(format!("{}", NamingStyle::SnakeCase), "snake_case");
153        assert_eq!(format!("{}", NamingStyle::CamelCase), "camelCase");
154        assert_eq!(format!("{}", NamingStyle::PascalCase), "PascalCase");
155    }
156
157    #[test]
158    fn test_project_context_default() {
159        let context = ProjectContext::default();
160        assert!(context.languages.is_empty());
161        assert!(context.project_type.is_none());
162    }
163}