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 | AstLanguage::JavaScript | AstLanguage::TypeScript
124 | AstLanguage::Go | AstLanguage::Rust => LanguageTier::FullAst,
125
126 AstLanguage::Html => LanguageTier::SyntaxAware,
128
129 AstLanguage::Java | AstLanguage::C | AstLanguage::Cpp
131 | AstLanguage::Ruby | AstLanguage::CSharp => LanguageTier::Future,
132 }
133 }
134
135 pub fn features(&self) -> LanguageFeatures {
137 match self {
138 AstLanguage::Python => LanguageFeatures {
139 tier: LanguageTier::FullAst,
140 has_functions: true,
141 has_classes: true,
142 has_documentation: true,
143 has_imports: true,
144 complexity_factors: vec![
145 "list_comprehensions".to_string(),
146 "decorators".to_string(),
147 "async_await".to_string(),
148 "generators".to_string(),
149 ],
150 extensions: vec!["py".to_string(), "pyi".to_string(), "pyw".to_string()],
151 },
152
153 AstLanguage::JavaScript => LanguageFeatures {
154 tier: LanguageTier::FullAst,
155 has_functions: true,
156 has_classes: true,
157 has_documentation: true,
158 has_imports: true,
159 complexity_factors: vec![
160 "closures".to_string(),
161 "promises".to_string(),
162 "async_await".to_string(),
163 "prototypal_inheritance".to_string(),
164 ],
165 extensions: vec!["js".to_string(), "mjs".to_string(), "cjs".to_string()],
166 },
167
168 AstLanguage::TypeScript => LanguageFeatures {
169 tier: LanguageTier::FullAst,
170 has_functions: true,
171 has_classes: true,
172 has_documentation: true,
173 has_imports: true,
174 complexity_factors: vec![
175 "generic_types".to_string(),
176 "type_guards".to_string(),
177 "conditional_types".to_string(),
178 "mapped_types".to_string(),
179 ],
180 extensions: vec!["ts".to_string(), "tsx".to_string(), "mts".to_string()],
181 },
182
183 AstLanguage::Rust => LanguageFeatures {
184 tier: LanguageTier::FullAst,
185 has_functions: true,
186 has_classes: false, has_documentation: true,
188 has_imports: true,
189 complexity_factors: vec![
190 "lifetimes".to_string(),
191 "borrowing".to_string(),
192 "pattern_matching".to_string(),
193 "macros".to_string(),
194 ],
195 extensions: vec!["rs".to_string()],
196 },
197
198 AstLanguage::Go => LanguageFeatures {
199 tier: LanguageTier::FullAst,
200 has_functions: true,
201 has_classes: false, has_documentation: true,
203 has_imports: true,
204 complexity_factors: vec![
205 "goroutines".to_string(),
206 "channels".to_string(),
207 "interfaces".to_string(),
208 "defer_statements".to_string(),
209 ],
210 extensions: vec!["go".to_string()],
211 },
212
213 AstLanguage::Java => LanguageFeatures {
214 tier: LanguageTier::FullAst,
215 has_functions: true,
216 has_classes: true,
217 has_documentation: true,
218 has_imports: true,
219 complexity_factors: vec![
220 "inheritance".to_string(),
221 "generics".to_string(),
222 "reflection".to_string(),
223 "annotations".to_string(),
224 ],
225 extensions: vec!["java".to_string()],
226 },
227
228 _ => LanguageFeatures {
230 tier: self.tier(),
231 has_functions: false,
232 has_classes: false,
233 has_documentation: false,
234 has_imports: false,
235 complexity_factors: vec![],
236 extensions: vec![],
237 },
238 }
239 }
240
241 pub fn all_supported() -> Vec<Self> {
243 vec![
244 AstLanguage::Python,
246 AstLanguage::JavaScript,
247 AstLanguage::TypeScript,
248 AstLanguage::Go,
249 AstLanguage::Rust,
250 AstLanguage::Html,
251
252 AstLanguage::Java,
254 AstLanguage::C,
255 AstLanguage::Cpp,
256 AstLanguage::Ruby,
257 AstLanguage::CSharp,
258 ]
259 }
260
261 pub fn name(&self) -> &'static str {
263 match self {
264 AstLanguage::Python => "Python",
265 AstLanguage::JavaScript => "JavaScript",
266 AstLanguage::TypeScript => "TypeScript",
267 AstLanguage::Go => "Go",
268 AstLanguage::Rust => "Rust",
269 AstLanguage::Html => "HTML",
270 AstLanguage::Java => "Java",
271 AstLanguage::C => "C",
272 AstLanguage::Cpp => "C++",
273 AstLanguage::Ruby => "Ruby",
274 AstLanguage::CSharp => "C#",
275 }
276 }
277}
278
279#[derive(Debug, Clone, Serialize, Deserialize)]
281pub struct LanguageStats {
282 pub total_languages: usize,
284 pub by_tier: HashMap<LanguageTier, usize>,
286 pub ast_supported: usize,
288 pub tree_sitter_available: usize,
290}
291
292impl LanguageStats {
293 pub fn calculate() -> Self {
295 let all_languages = AstLanguage::all_supported();
296 let total_languages = all_languages.len();
297
298 let mut by_tier = HashMap::new();
299 let mut ast_supported = 0;
300 let mut tree_sitter_available = 0;
301
302 for language in &all_languages {
303 let tier = language.tier();
304 *by_tier.entry(tier).or_insert(0) += 1;
305
306 if tier == LanguageTier::FullAst || tier == LanguageTier::SyntaxAware {
307 ast_supported += 1;
308 }
309
310 #[cfg(feature = "tree-sitter")]
311 if language.tree_sitter_language().is_some() {
312 tree_sitter_available += 1;
313 }
314 }
315
316 Self {
317 total_languages,
318 by_tier,
319 ast_supported,
320 tree_sitter_available,
321 }
322 }
323}
324
325#[cfg(test)]
326mod tests {
327 use super::*;
328
329 #[test]
330 fn test_language_detection() {
331 assert_eq!(AstLanguage::from_extension("py"), Some(AstLanguage::Python));
332 assert_eq!(AstLanguage::from_extension("js"), Some(AstLanguage::JavaScript));
333 assert_eq!(AstLanguage::from_extension("rs"), Some(AstLanguage::Rust));
334 assert_eq!(AstLanguage::from_extension("go"), Some(AstLanguage::Go));
335 assert_eq!(AstLanguage::from_extension("unknown"), None);
336 }
337
338 #[test]
339 fn test_language_tiers() {
340 assert_eq!(AstLanguage::Python.tier(), LanguageTier::FullAst);
341 assert_eq!(AstLanguage::Html.tier(), LanguageTier::SyntaxAware);
342 assert_eq!(AstLanguage::Java.tier(), LanguageTier::Future);
343 }
344
345 #[test]
346 fn test_language_features() {
347 let python_features = AstLanguage::Python.features();
348 assert!(python_features.has_functions);
349 assert!(python_features.has_classes);
350 assert!(python_features.has_documentation);
351 assert!(python_features.has_imports);
352 assert!(!python_features.complexity_factors.is_empty());
353 }
354
355 #[test]
356 fn test_language_count() {
357 let all_languages = AstLanguage::all_supported();
358 assert_eq!(all_languages.len(), 11, "Expected 11 languages, got {}", all_languages.len());
360 }
361
362 #[test]
363 fn test_language_stats() {
364 let stats = LanguageStats::calculate();
365 assert_eq!(stats.total_languages, 11);
366 assert!(stats.by_tier.contains_key(&LanguageTier::FullAst));
367 assert!(stats.by_tier.contains_key(&LanguageTier::SyntaxAware));
368 assert!(stats.by_tier.contains_key(&LanguageTier::Future));
369 }
370}