1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11pub enum AstLanguage {
12 Python,
14 JavaScript,
15 TypeScript,
16 Go,
17 Rust,
18 Html,
19
20 Java,
22 C,
23 Cpp,
24 Ruby,
25 CSharp,
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
30pub enum LanguageTier {
31 FullAst,
33 SyntaxAware,
35 Future,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct LanguageFeatures {
42 pub tier: LanguageTier,
44 pub has_functions: bool,
46 pub has_classes: bool,
48 pub has_documentation: bool,
50 pub has_imports: bool,
52 pub complexity_factors: Vec<String>,
54 pub extensions: Vec<String>,
56}
57
58impl AstLanguage {
59 #[cfg(feature = "tree-sitter")]
61 pub fn tree_sitter_language(&self) -> Option<tree_sitter::Language> {
62 match self {
63 AstLanguage::Python => Some(tree_sitter_python::language()),
65 AstLanguage::JavaScript => Some(tree_sitter_javascript::language()),
66 AstLanguage::TypeScript => Some(tree_sitter_typescript::language_typescript()),
67 AstLanguage::Go => Some(tree_sitter_go::language()),
68 AstLanguage::Rust => Some(tree_sitter_rust::language()),
69 AstLanguage::Html => Some(tree_sitter_html::language()),
70
71 AstLanguage::Java => None, AstLanguage::CSharp => None, AstLanguage::C => None, AstLanguage::Cpp => None, AstLanguage::PHP => None, AstLanguage::Ruby => None, AstLanguage::Swift => None, AstLanguage::Kotlin => None, AstLanguage::Css => None, AstLanguage::Json => None, AstLanguage::Yaml => None, AstLanguage::Xml => None, AstLanguage::Markdown => None, AstLanguage::Sql => None, AstLanguage::Bash => None, AstLanguage::PowerShell => None, AstLanguage::Dockerfile => None, _ => None,
94 }
95 }
96
97 pub fn from_extension(ext: &str) -> Option<Self> {
99 match ext.to_lowercase().as_str() {
100 "py" | "pyi" | "pyw" => Some(AstLanguage::Python),
102 "js" | "mjs" | "cjs" => Some(AstLanguage::JavaScript),
103 "ts" | "mts" | "cts" | "tsx" => Some(AstLanguage::TypeScript),
104 "go" => Some(AstLanguage::Go),
105 "rs" => Some(AstLanguage::Rust),
106 "html" | "htm" => Some(AstLanguage::Html),
107
108 "java" => Some(AstLanguage::Java),
110 "c" => Some(AstLanguage::C),
111 "cpp" | "cc" | "cxx" | "c++" | "hpp" | "h" => Some(AstLanguage::Cpp),
112 "rb" | "ruby" => Some(AstLanguage::Ruby),
113 "cs" => Some(AstLanguage::CSharp),
114
115 _ => None,
116 }
117 }
118
119 pub fn tier(&self) -> LanguageTier {
121 match self {
122 AstLanguage::Python
124 | AstLanguage::JavaScript
125 | AstLanguage::TypeScript
126 | AstLanguage::Go
127 | AstLanguage::Rust => LanguageTier::FullAst,
128
129 AstLanguage::Html => LanguageTier::SyntaxAware,
131
132 AstLanguage::Java
134 | AstLanguage::C
135 | AstLanguage::Cpp
136 | AstLanguage::Ruby
137 | AstLanguage::CSharp => LanguageTier::Future,
138 }
139 }
140
141 pub fn features(&self) -> LanguageFeatures {
143 match self {
144 AstLanguage::Python => LanguageFeatures {
145 tier: LanguageTier::FullAst,
146 has_functions: true,
147 has_classes: true,
148 has_documentation: true,
149 has_imports: true,
150 complexity_factors: vec![
151 "list_comprehensions".to_string(),
152 "decorators".to_string(),
153 "async_await".to_string(),
154 "generators".to_string(),
155 ],
156 extensions: vec!["py".to_string(), "pyi".to_string(), "pyw".to_string()],
157 },
158
159 AstLanguage::JavaScript => LanguageFeatures {
160 tier: LanguageTier::FullAst,
161 has_functions: true,
162 has_classes: true,
163 has_documentation: true,
164 has_imports: true,
165 complexity_factors: vec![
166 "closures".to_string(),
167 "promises".to_string(),
168 "async_await".to_string(),
169 "prototypal_inheritance".to_string(),
170 ],
171 extensions: vec!["js".to_string(), "mjs".to_string(), "cjs".to_string()],
172 },
173
174 AstLanguage::TypeScript => LanguageFeatures {
175 tier: LanguageTier::FullAst,
176 has_functions: true,
177 has_classes: true,
178 has_documentation: true,
179 has_imports: true,
180 complexity_factors: vec![
181 "generic_types".to_string(),
182 "type_guards".to_string(),
183 "conditional_types".to_string(),
184 "mapped_types".to_string(),
185 ],
186 extensions: vec!["ts".to_string(), "tsx".to_string(), "mts".to_string()],
187 },
188
189 AstLanguage::Rust => LanguageFeatures {
190 tier: LanguageTier::FullAst,
191 has_functions: true,
192 has_classes: false, has_documentation: true,
194 has_imports: true,
195 complexity_factors: vec![
196 "lifetimes".to_string(),
197 "borrowing".to_string(),
198 "pattern_matching".to_string(),
199 "macros".to_string(),
200 ],
201 extensions: vec!["rs".to_string()],
202 },
203
204 AstLanguage::Go => LanguageFeatures {
205 tier: LanguageTier::FullAst,
206 has_functions: true,
207 has_classes: false, has_documentation: true,
209 has_imports: true,
210 complexity_factors: vec![
211 "goroutines".to_string(),
212 "channels".to_string(),
213 "interfaces".to_string(),
214 "defer_statements".to_string(),
215 ],
216 extensions: vec!["go".to_string()],
217 },
218
219 AstLanguage::Java => LanguageFeatures {
220 tier: LanguageTier::FullAst,
221 has_functions: true,
222 has_classes: true,
223 has_documentation: true,
224 has_imports: true,
225 complexity_factors: vec![
226 "inheritance".to_string(),
227 "generics".to_string(),
228 "reflection".to_string(),
229 "annotations".to_string(),
230 ],
231 extensions: vec!["java".to_string()],
232 },
233
234 _ => LanguageFeatures {
236 tier: self.tier(),
237 has_functions: false,
238 has_classes: false,
239 has_documentation: false,
240 has_imports: false,
241 complexity_factors: vec![],
242 extensions: vec![],
243 },
244 }
245 }
246
247 pub fn all_supported() -> Vec<Self> {
249 vec![
250 AstLanguage::Python,
252 AstLanguage::JavaScript,
253 AstLanguage::TypeScript,
254 AstLanguage::Go,
255 AstLanguage::Rust,
256 AstLanguage::Html,
257 AstLanguage::Java,
259 AstLanguage::C,
260 AstLanguage::Cpp,
261 AstLanguage::Ruby,
262 AstLanguage::CSharp,
263 ]
264 }
265
266 pub fn name(&self) -> &'static str {
268 match self {
269 AstLanguage::Python => "Python",
270 AstLanguage::JavaScript => "JavaScript",
271 AstLanguage::TypeScript => "TypeScript",
272 AstLanguage::Go => "Go",
273 AstLanguage::Rust => "Rust",
274 AstLanguage::Html => "HTML",
275 AstLanguage::Java => "Java",
276 AstLanguage::C => "C",
277 AstLanguage::Cpp => "C++",
278 AstLanguage::Ruby => "Ruby",
279 AstLanguage::CSharp => "C#",
280 }
281 }
282}
283
284#[derive(Debug, Clone, Serialize, Deserialize)]
286pub struct LanguageStats {
287 pub total_languages: usize,
289 pub by_tier: HashMap<LanguageTier, usize>,
291 pub ast_supported: usize,
293 pub tree_sitter_available: usize,
295}
296
297impl LanguageStats {
298 pub fn calculate() -> Self {
300 let all_languages = AstLanguage::all_supported();
301 let total_languages = all_languages.len();
302
303 let mut by_tier = HashMap::new();
304 let mut ast_supported = 0;
305 let mut tree_sitter_available = 0;
306
307 for language in &all_languages {
308 let tier = language.tier();
309 *by_tier.entry(tier).or_insert(0) += 1;
310
311 if tier == LanguageTier::FullAst || tier == LanguageTier::SyntaxAware {
312 ast_supported += 1;
313 }
314
315 #[cfg(feature = "tree-sitter")]
316 if language.tree_sitter_language().is_some() {
317 tree_sitter_available += 1;
318 }
319 }
320
321 Self {
322 total_languages,
323 by_tier,
324 ast_supported,
325 tree_sitter_available,
326 }
327 }
328}
329
330#[cfg(test)]
331mod tests {
332 use super::*;
333
334 #[test]
335 fn test_language_detection() {
336 assert_eq!(AstLanguage::from_extension("py"), Some(AstLanguage::Python));
337 assert_eq!(
338 AstLanguage::from_extension("js"),
339 Some(AstLanguage::JavaScript)
340 );
341 assert_eq!(AstLanguage::from_extension("rs"), Some(AstLanguage::Rust));
342 assert_eq!(AstLanguage::from_extension("go"), Some(AstLanguage::Go));
343 assert_eq!(AstLanguage::from_extension("unknown"), None);
344 }
345
346 #[test]
347 fn test_language_tiers() {
348 assert_eq!(AstLanguage::Python.tier(), LanguageTier::FullAst);
349 assert_eq!(AstLanguage::Html.tier(), LanguageTier::SyntaxAware);
350 assert_eq!(AstLanguage::Java.tier(), LanguageTier::Future);
351 }
352
353 #[test]
354 fn test_language_features() {
355 let python_features = AstLanguage::Python.features();
356 assert!(python_features.has_functions);
357 assert!(python_features.has_classes);
358 assert!(python_features.has_documentation);
359 assert!(python_features.has_imports);
360 assert!(!python_features.complexity_factors.is_empty());
361 }
362
363 #[test]
364 fn test_language_count() {
365 let all_languages = AstLanguage::all_supported();
366 assert_eq!(
368 all_languages.len(),
369 11,
370 "Expected 11 languages, got {}",
371 all_languages.len()
372 );
373 }
374
375 #[test]
376 fn test_language_stats() {
377 let stats = LanguageStats::calculate();
378 assert_eq!(stats.total_languages, 11);
379 assert!(stats.by_tier.contains_key(&LanguageTier::FullAst));
380 assert!(stats.by_tier.contains_key(&LanguageTier::SyntaxAware));
381 assert!(stats.by_tier.contains_key(&LanguageTier::Future));
382 }
383}