Skip to main content

gitcortex_indexer/parser/
mod.rs

1use std::path::Path;
2
3use gitcortex_core::{
4    error::Result,
5    graph::{Edge, Node, NodeId},
6};
7
8pub mod go;
9pub mod python;
10pub mod rust;
11pub mod typescript;
12
13/// Result of parsing a single source file.
14pub struct ParseResult {
15    pub nodes: Vec<Node>,
16    pub edges: Vec<Edge>,
17    /// Unresolved call sites — resolved cross-file by the indexer.
18    pub deferred_calls: Vec<(NodeId, String)>,
19    /// Unresolved parameter/return-type references: (fn_id, type_name).
20    pub deferred_uses: Vec<(NodeId, String)>,
21    /// Unresolved trait implementations: (struct_id, trait_name).
22    pub deferred_implements: Vec<(NodeId, String)>,
23    /// Unresolved use-declaration imports: (src_node_id, imported_leaf_name).
24    pub deferred_imports: Vec<(NodeId, String)>,
25}
26
27/// Contract every language parser must satisfy.
28///
29/// Implementations are stateless — a parser value is cheap to create and safe
30/// to reuse across files. Parsing is purely functional: source text in, graph
31/// nodes + edges out. Cross-file edges are resolved by the indexer after all
32/// files in the diff have been parsed.
33pub trait LanguageParser: Send + Sync {
34    /// File extensions this parser handles (lower-case, without the dot).
35    fn extensions(&self) -> &[&str];
36
37    /// Parse `source` (content of `path`) and return all nodes, edges, and
38    /// unresolved call references found in that file.
39    fn parse(&self, path: &Path, source: &str) -> Result<ParseResult>;
40}
41
42/// Return the appropriate parser for `path`, keyed on file extension.
43/// Returns `None` when the extension is unsupported.
44pub fn parser_for_path(path: &Path) -> Option<Box<dyn LanguageParser>> {
45    let ext = path.extension()?.to_str()?;
46    match ext {
47        "rs" => Some(Box::new(rust::RustParser::new())),
48        "py" => Some(Box::new(python::PythonParser::new())),
49        "ts" => Some(Box::new(typescript::TypeScriptParser::new_ts())),
50        "tsx" => Some(Box::new(typescript::TypeScriptParser::new_tsx())),
51        "js" | "mjs" | "cjs" => Some(Box::new(typescript::JavaScriptParser::new())),
52        "jsx" => Some(Box::new(typescript::JavaScriptParser::new())),
53        "go" => Some(Box::new(go::GoParser::new())),
54        _ => None,
55    }
56}