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