use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum AstLanguage {
Python,
JavaScript,
TypeScript,
Go,
Rust,
Html,
Java,
C,
Cpp,
Ruby,
CSharp,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum LanguageTier {
FullAst,
SyntaxAware,
Future,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LanguageFeatures {
pub tier: LanguageTier,
pub has_functions: bool,
pub has_classes: bool,
pub has_documentation: bool,
pub has_imports: bool,
pub complexity_factors: Vec<String>,
pub extensions: Vec<String>,
}
impl AstLanguage {
#[cfg(feature = "tree-sitter")]
pub fn tree_sitter_language(&self) -> Option<tree_sitter::Language> {
match self {
AstLanguage::Python => Some(tree_sitter_python::language()),
AstLanguage::JavaScript => Some(tree_sitter_javascript::language()),
AstLanguage::TypeScript => Some(tree_sitter_typescript::language_typescript()),
AstLanguage::Go => Some(tree_sitter_go::language()),
AstLanguage::Rust => Some(tree_sitter_rust::language()),
AstLanguage::Html => Some(tree_sitter_html::language()),
AstLanguage::Java => None, AstLanguage::CSharp => None, AstLanguage::C => None, AstLanguage::Cpp => None, AstLanguage::Ruby => None, }
}
pub fn from_extension(ext: &str) -> Option<Self> {
match ext.to_lowercase().as_str() {
"py" | "pyi" | "pyw" => Some(AstLanguage::Python),
"js" | "mjs" | "cjs" => Some(AstLanguage::JavaScript),
"ts" | "mts" | "cts" | "tsx" => Some(AstLanguage::TypeScript),
"go" => Some(AstLanguage::Go),
"rs" => Some(AstLanguage::Rust),
"html" | "htm" => Some(AstLanguage::Html),
"java" => Some(AstLanguage::Java),
"c" => Some(AstLanguage::C),
"cpp" | "cc" | "cxx" | "c++" | "hpp" | "h" => Some(AstLanguage::Cpp),
"rb" | "ruby" => Some(AstLanguage::Ruby),
"cs" => Some(AstLanguage::CSharp),
_ => None,
}
}
pub fn tier(&self) -> LanguageTier {
match self {
AstLanguage::Python
| AstLanguage::JavaScript
| AstLanguage::TypeScript
| AstLanguage::Go
| AstLanguage::Rust => LanguageTier::FullAst,
AstLanguage::Html => LanguageTier::SyntaxAware,
AstLanguage::Java
| AstLanguage::C
| AstLanguage::Cpp
| AstLanguage::Ruby
| AstLanguage::CSharp => LanguageTier::Future,
}
}
pub fn features(&self) -> LanguageFeatures {
match self {
AstLanguage::Python => LanguageFeatures {
tier: LanguageTier::FullAst,
has_functions: true,
has_classes: true,
has_documentation: true,
has_imports: true,
complexity_factors: vec![
"list_comprehensions".to_string(),
"decorators".to_string(),
"async_await".to_string(),
"generators".to_string(),
],
extensions: vec!["py".to_string(), "pyi".to_string(), "pyw".to_string()],
},
AstLanguage::JavaScript => LanguageFeatures {
tier: LanguageTier::FullAst,
has_functions: true,
has_classes: true,
has_documentation: true,
has_imports: true,
complexity_factors: vec![
"closures".to_string(),
"promises".to_string(),
"async_await".to_string(),
"prototypal_inheritance".to_string(),
],
extensions: vec!["js".to_string(), "mjs".to_string(), "cjs".to_string()],
},
AstLanguage::TypeScript => LanguageFeatures {
tier: LanguageTier::FullAst,
has_functions: true,
has_classes: true,
has_documentation: true,
has_imports: true,
complexity_factors: vec![
"generic_types".to_string(),
"type_guards".to_string(),
"conditional_types".to_string(),
"mapped_types".to_string(),
],
extensions: vec!["ts".to_string(), "tsx".to_string(), "mts".to_string()],
},
AstLanguage::Rust => LanguageFeatures {
tier: LanguageTier::FullAst,
has_functions: true,
has_classes: false, has_documentation: true,
has_imports: true,
complexity_factors: vec![
"lifetimes".to_string(),
"borrowing".to_string(),
"pattern_matching".to_string(),
"macros".to_string(),
],
extensions: vec!["rs".to_string()],
},
AstLanguage::Go => LanguageFeatures {
tier: LanguageTier::FullAst,
has_functions: true,
has_classes: false, has_documentation: true,
has_imports: true,
complexity_factors: vec![
"goroutines".to_string(),
"channels".to_string(),
"interfaces".to_string(),
"defer_statements".to_string(),
],
extensions: vec!["go".to_string()],
},
AstLanguage::Java => LanguageFeatures {
tier: LanguageTier::FullAst,
has_functions: true,
has_classes: true,
has_documentation: true,
has_imports: true,
complexity_factors: vec![
"inheritance".to_string(),
"generics".to_string(),
"reflection".to_string(),
"annotations".to_string(),
],
extensions: vec!["java".to_string()],
},
_ => LanguageFeatures {
tier: self.tier(),
has_functions: false,
has_classes: false,
has_documentation: false,
has_imports: false,
complexity_factors: vec![],
extensions: vec![],
},
}
}
pub fn all_supported() -> Vec<Self> {
vec![
AstLanguage::Python,
AstLanguage::JavaScript,
AstLanguage::TypeScript,
AstLanguage::Go,
AstLanguage::Rust,
AstLanguage::Html,
AstLanguage::Java,
AstLanguage::C,
AstLanguage::Cpp,
AstLanguage::Ruby,
AstLanguage::CSharp,
]
}
pub fn name(&self) -> &'static str {
match self {
AstLanguage::Python => "Python",
AstLanguage::JavaScript => "JavaScript",
AstLanguage::TypeScript => "TypeScript",
AstLanguage::Go => "Go",
AstLanguage::Rust => "Rust",
AstLanguage::Html => "HTML",
AstLanguage::Java => "Java",
AstLanguage::C => "C",
AstLanguage::Cpp => "C++",
AstLanguage::Ruby => "Ruby",
AstLanguage::CSharp => "C#",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LanguageStats {
pub total_languages: usize,
pub by_tier: HashMap<LanguageTier, usize>,
pub ast_supported: usize,
pub tree_sitter_available: usize,
}
impl LanguageStats {
pub fn calculate() -> Self {
let all_languages = AstLanguage::all_supported();
let total_languages = all_languages.len();
let mut by_tier = HashMap::new();
let mut ast_supported = 0;
let mut tree_sitter_available = 0;
for language in &all_languages {
let tier = language.tier();
*by_tier.entry(tier).or_insert(0) += 1;
if tier == LanguageTier::FullAst || tier == LanguageTier::SyntaxAware {
ast_supported += 1;
}
#[cfg(feature = "tree-sitter")]
if language.tree_sitter_language().is_some() {
tree_sitter_available += 1;
}
}
Self {
total_languages,
by_tier,
ast_supported,
tree_sitter_available,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_language_detection() {
assert_eq!(AstLanguage::from_extension("py"), Some(AstLanguage::Python));
assert_eq!(
AstLanguage::from_extension("js"),
Some(AstLanguage::JavaScript)
);
assert_eq!(AstLanguage::from_extension("rs"), Some(AstLanguage::Rust));
assert_eq!(AstLanguage::from_extension("go"), Some(AstLanguage::Go));
assert_eq!(AstLanguage::from_extension("unknown"), None);
}
#[test]
fn test_language_tiers() {
assert_eq!(AstLanguage::Python.tier(), LanguageTier::FullAst);
assert_eq!(AstLanguage::Html.tier(), LanguageTier::SyntaxAware);
assert_eq!(AstLanguage::Java.tier(), LanguageTier::Future);
}
#[test]
fn test_language_features() {
let python_features = AstLanguage::Python.features();
assert!(python_features.has_functions);
assert!(python_features.has_classes);
assert!(python_features.has_documentation);
assert!(python_features.has_imports);
assert!(!python_features.complexity_factors.is_empty());
}
#[test]
fn test_language_count() {
let all_languages = AstLanguage::all_supported();
assert_eq!(
all_languages.len(),
11,
"Expected 11 languages, got {}",
all_languages.len()
);
}
#[test]
fn test_language_stats() {
let stats = LanguageStats::calculate();
assert_eq!(stats.total_languages, 11);
assert!(stats.by_tier.contains_key(&LanguageTier::FullAst));
assert!(stats.by_tier.contains_key(&LanguageTier::SyntaxAware));
assert!(stats.by_tier.contains_key(&LanguageTier::Future));
}
}