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
8mod complexity;
9mod deftext;
10pub mod go;
11pub mod java;
12pub mod markdown;
13pub mod python;
14pub mod rust;
15pub mod typescript;
16
17pub(crate) use complexity::cyclomatic_complexity;
18
19pub(crate) use deftext::capture as capture_definition;
20
21/// Result of parsing a single source file.
22pub struct ParseResult {
23    pub nodes: Vec<Node>,
24    pub edges: Vec<Edge>,
25    /// Unresolved call sites — resolved cross-file by the indexer.
26    pub deferred_calls: Vec<(NodeId, String, u32)>,
27    /// Unresolved parameter/return-type references: (fn_id, type_name).
28    pub deferred_uses: Vec<(NodeId, String)>,
29    /// Unresolved trait implementations: (struct_id, trait_name).
30    pub deferred_implements: Vec<(NodeId, String)>,
31    /// Unresolved class extends / structural inheritance: (subtype_id, supertype_name).
32    pub deferred_inherits: Vec<(NodeId, String)>,
33    /// Unresolved exception throws: (method_id, exception_type_name).
34    pub deferred_throws: Vec<(NodeId, String)>,
35    /// Unresolved decorator/annotation references: (target_id, annotation_name).
36    pub deferred_annotated: Vec<(NodeId, String)>,
37    /// Unresolved use-declaration imports: (src_node_id, imported_leaf_name).
38    pub deferred_imports: Vec<(NodeId, String)>,
39    /// Unresolved Markdown doc→code symbol references: (section_or_file_id, symbol_name).
40    /// Intentionally cross-language — see `EdgeKind::References`.
41    pub deferred_doc_refs: Vec<(NodeId, String)>,
42}
43
44/// Contract every language parser must satisfy.
45///
46/// Implementations are stateless — a parser value is cheap to create and safe
47/// to reuse across files. Parsing is purely functional: source text in, graph
48/// nodes + edges out. Cross-file edges are resolved by the indexer after all
49/// files in the diff have been parsed.
50pub trait LanguageParser: Send + Sync {
51    /// File extensions this parser handles (lower-case, without the dot).
52    fn extensions(&self) -> &[&str];
53
54    /// Parse `source` (content of `path`) and return all nodes, edges, and
55    /// unresolved call references found in that file.
56    fn parse(&self, path: &Path, source: &str) -> Result<ParseResult>;
57}
58
59/// Return the appropriate parser for `path`, keyed on file extension.
60/// Returns `None` when the extension is unsupported.
61pub fn parser_for_path(path: &Path) -> Option<Box<dyn LanguageParser>> {
62    let ext = path.extension()?.to_str()?;
63    match ext {
64        "rs" => Some(Box::new(rust::RustParser::new())),
65        "py" => Some(Box::new(python::PythonParser::new())),
66        "ts" => Some(Box::new(typescript::TypeScriptParser::new_ts())),
67        "tsx" => Some(Box::new(typescript::TypeScriptParser::new_tsx())),
68        "js" | "mjs" | "cjs" => Some(Box::new(typescript::JavaScriptParser::new())),
69        "jsx" => Some(Box::new(typescript::JavaScriptParser::new())),
70        "go" => Some(Box::new(go::GoParser::new())),
71        "java" => Some(Box::new(java::JavaParser::new())),
72        "md" | "markdown" => Some(Box::new(markdown::MarkdownParser::new())),
73        _ => None,
74    }
75}