pub mod c_base;
pub mod definitions;
use std::collections::HashMap;
use std::sync::OnceLock;
pub trait Language: Send + Sync {
fn name(&self) -> &'static str;
fn extensions(&self) -> &'static [&'static str];
fn line_comment(&self) -> Option<&'static str>;
fn block_comment(&self) -> Option<(&'static str, &'static str)>;
fn import_keywords(&self) -> &'static [&'static str];
fn function_keywords(&self) -> &'static [&'static str];
fn indent_size(&self) -> usize {
4
}
fn default_thresholds(&self) -> crate::Thresholds {
crate::Thresholds::default()
}
fn count_imports(&self, content: &str) -> usize {
let keywords = self.import_keywords();
content
.lines()
.filter(|line| {
let trimmed = line.trim_start();
if trimmed.is_empty() {
return false;
}
keywords.iter().any(|&kw| {
if kw.ends_with('(') || kw.ends_with('"') {
trimmed.contains(kw)
} else {
trimmed.starts_with(kw)
}
})
})
.count()
}
fn count_functions(&self, content: &str) -> usize {
let keywords = self.function_keywords();
content
.lines()
.filter(|line| {
let trimmed = line.trim_start();
keywords.iter().any(|&kw| trimmed.starts_with(kw))
})
.count()
}
}
pub struct LanguageRegistry {
languages: Vec<Box<dyn Language>>,
extension_map: HashMap<&'static str, usize>,
}
static REGISTRY: OnceLock<LanguageRegistry> = OnceLock::new();
impl LanguageRegistry {
#[must_use]
pub fn get() -> &'static Self {
REGISTRY.get_or_init(Self::new)
}
fn new() -> Self {
let languages: Vec<Box<dyn Language>> = vec![
Box::new(definitions::rust::Rust),
Box::new(definitions::python::Python),
Box::new(definitions::javascript::JavaScript),
Box::new(definitions::typescript::TypeScript),
Box::new(definitions::java::Java),
Box::new(definitions::csharp::CSharp),
Box::new(definitions::gdscript::GDScript),
Box::new(definitions::lua::Lua),
Box::new(definitions::go::Go),
Box::new(definitions::php::PHP),
];
let mut extension_map = HashMap::new();
for (i, lang) in languages.iter().enumerate() {
for ext in lang.extensions() {
extension_map.insert(*ext, i);
}
}
Self {
languages,
extension_map,
}
}
#[must_use]
pub fn get_by_extension(&self, ext: &str) -> Option<&dyn Language> {
self.extension_map
.get(ext)
.map(|&i| self.languages[i].as_ref())
}
#[must_use]
pub fn supported_extensions(&self) -> Vec<&'static str> {
self.extension_map.keys().copied().collect()
}
}