arbor_core/languages/mod.rs
1//! Language parsers module.
2//!
3//! Each supported language has its own submodule that implements
4//! the LanguageParser trait. This keeps language-specific quirks
5//! isolated and makes it straightforward to add new languages.
6
7mod python;
8mod rust;
9mod typescript;
10
11use crate::node::CodeNode;
12
13/// Trait for language-specific parsing logic.
14///
15/// Each language needs to implement this to handle its unique AST
16/// structure and idioms. The trait provides the Tree-sitter language
17/// and the extraction logic.
18pub trait LanguageParser: Send + Sync {
19 /// Returns the Tree-sitter language for this parser.
20 fn language(&self) -> tree_sitter::Language;
21
22 /// File extensions this parser handles.
23 fn extensions(&self) -> &[&str];
24
25 /// Extracts CodeNodes from a parsed Tree-sitter tree.
26 ///
27 /// This is where the magic happens. Each language traverses
28 /// its AST differently to find functions, classes, etc.
29 fn extract_nodes(
30 &self,
31 tree: &tree_sitter::Tree,
32 source: &str,
33 file_path: &str,
34 ) -> Vec<CodeNode>;
35}
36
37/// Gets a parser for the given file extension.
38///
39/// Returns None if we don't support this extension.
40pub fn get_parser(extension: &str) -> Option<Box<dyn LanguageParser>> {
41 match extension.to_lowercase().as_str() {
42 // TypeScript and JavaScript
43 "ts" | "tsx" | "mts" | "cts" => Some(Box::new(typescript::TypeScriptParser)),
44 "js" | "jsx" | "mjs" | "cjs" => Some(Box::new(typescript::TypeScriptParser)),
45
46 // Rust
47 "rs" => Some(Box::new(rust::RustParser)),
48
49 // Python
50 "py" | "pyi" => Some(Box::new(python::PythonParser)),
51
52 _ => None,
53 }
54}
55
56/// Lists all supported file extensions.
57pub fn supported_extensions() -> &'static [&'static str] {
58 &[
59 "ts", "tsx", "mts", "cts", // TypeScript
60 "js", "jsx", "mjs", "cjs", // JavaScript
61 "rs", // Rust
62 "py", "pyi", // Python
63 ]
64}
65
66/// Checks if a file extension is supported.
67pub fn is_supported(extension: &str) -> bool {
68 get_parser(extension).is_some()
69}