scribe_analysis/language_support/
mod.rs1pub mod ast_language;
28pub mod documentation_analysis;
29pub mod function_extraction;
30pub mod language_metrics;
31
32pub use ast_language::{AstLanguage, LanguageFeatures, LanguageTier};
33pub use documentation_analysis::{DocumentationAnalyzer, DocumentationCoverage};
34pub use function_extraction::{ClassInfo, FunctionExtractor, FunctionInfo};
35pub use language_metrics::{LanguageMetrics, LanguageSpecificComplexity};
36
37use scribe_core::Result;
38use std::collections::HashMap;
39
40pub struct LanguageSupport {
42 function_extractors: HashMap<AstLanguage, FunctionExtractor>,
44 doc_analyzers: HashMap<AstLanguage, DocumentationAnalyzer>,
46}
47
48impl LanguageSupport {
49 pub fn new() -> Result<Self> {
51 let mut support = Self {
52 function_extractors: HashMap::new(),
53 doc_analyzers: HashMap::new(),
54 };
55
56 support.initialize_extractors()?;
58
59 Ok(support)
60 }
61
62 fn initialize_extractors(&mut self) -> Result<()> {
64 for language in AstLanguage::all_supported() {
65 self.function_extractors
66 .insert(language, FunctionExtractor::new(language)?);
67 self.doc_analyzers
68 .insert(language, DocumentationAnalyzer::new(language)?);
69 }
70 Ok(())
71 }
72
73 pub fn get_language_features(&self, language: AstLanguage) -> LanguageFeatures {
75 language.features()
76 }
77
78 pub fn extract_functions(
80 &mut self,
81 content: &str,
82 language: AstLanguage,
83 ) -> Result<Vec<FunctionInfo>> {
84 if let Some(extractor) = self.function_extractors.get_mut(&language) {
85 extractor.extract_functions(content)
86 } else {
87 Ok(Vec::new())
88 }
89 }
90
91 pub fn analyze_documentation(
93 &self,
94 content: &str,
95 language: AstLanguage,
96 ) -> Result<DocumentationCoverage> {
97 if let Some(analyzer) = self.doc_analyzers.get(&language) {
98 analyzer.analyze_coverage(content)
99 } else {
100 Ok(DocumentationCoverage::default())
101 }
102 }
103
104 pub fn calculate_language_metrics(
106 &self,
107 content: &str,
108 language: AstLanguage,
109 ) -> Result<LanguageMetrics> {
110 LanguageMetrics::calculate(content, language)
111 }
112
113 pub fn is_supported(&self, language: AstLanguage) -> bool {
115 self.function_extractors.contains_key(&language)
116 }
117
118 pub fn supported_languages(&self) -> Vec<AstLanguage> {
120 self.function_extractors.keys().copied().collect()
121 }
122}
123
124impl Default for LanguageSupport {
125 fn default() -> Self {
126 Self::new().expect("Failed to create LanguageSupport")
127 }
128}
129
130#[derive(Debug, Clone)]
132pub struct LanguageAnalysisResult {
133 pub language: AstLanguage,
135 pub tier: LanguageTier,
137 pub functions: Vec<FunctionInfo>,
139 pub documentation: DocumentationCoverage,
141 pub metrics: LanguageMetrics,
143}
144
145pub fn analyze_file_language(
147 content: &str,
148 file_extension: &str,
149 language_support: &mut LanguageSupport,
150) -> Result<Option<LanguageAnalysisResult>> {
151 let language = match AstLanguage::from_extension(file_extension) {
152 Some(lang) => lang,
153 None => return Ok(None),
154 };
155
156 if !language_support.is_supported(language) {
157 return Ok(None);
158 }
159
160 let functions = language_support.extract_functions(content, language)?;
161 let documentation = language_support.analyze_documentation(content, language)?;
162 let metrics = language_support.calculate_language_metrics(content, language)?;
163
164 Ok(Some(LanguageAnalysisResult {
165 language,
166 tier: language.tier(),
167 functions,
168 documentation,
169 metrics,
170 }))
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176
177 #[test]
178 fn test_language_support_creation() {
179 let support = LanguageSupport::new();
180 if let Err(e) = &support {
181 println!("Error creating LanguageSupport: {:?}", e);
182 }
183 assert!(support.is_ok());
184 }
185
186 #[test]
187 fn test_supported_languages() {
188 let support = LanguageSupport::new().unwrap();
189 let languages = support.supported_languages();
190
191 assert!(languages.contains(&AstLanguage::Python));
193 assert!(languages.contains(&AstLanguage::JavaScript));
194 assert!(languages.contains(&AstLanguage::Rust));
195 }
196
197 #[test]
198 fn test_python_function_extraction() {
199 let mut support = LanguageSupport::new().unwrap();
200 let python_code = r#"
201 def hello_world():
202 """A simple function."""
203 print("Hello, World!")
204
205 class Calculator:
206 """A simple calculator class."""
207
208 def add(self, a, b):
209 """Add two numbers."""
210 return a + b
211 "#;
212
213 let functions = support.extract_functions(python_code, AstLanguage::Python);
214 assert!(functions.is_ok());
215 let functions = functions.unwrap();
216 assert!(!functions.is_empty());
217 }
218
219 #[test]
220 fn test_language_analysis() {
221 let mut support = LanguageSupport::new().unwrap();
222 let result = analyze_file_language("def test(): pass", "py", &mut support);
223
224 assert!(result.is_ok());
225 let result = result.unwrap();
226 assert!(result.is_some());
227 assert_eq!(result.unwrap().language, AstLanguage::Python);
228 }
229}