Skip to main content

codemov_parser/
rust.rs

1use codemov_core::{ImportEdge, ImportKind, Symbol, SymbolKind};
2use tree_sitter::Node;
3
4use crate::ParseError;
5
6pub fn extract(source: &[u8]) -> Result<Vec<Symbol>, ParseError> {
7    let mut parser = tree_sitter::Parser::new();
8    parser
9        .set_language(&tree_sitter_rust::language())
10        .map_err(|e| ParseError::Parse(e.to_string()))?;
11
12    let tree = parser
13        .parse(source, None)
14        .ok_or_else(|| ParseError::Parse("tree-sitter returned None".into()))?;
15
16    let mut symbols = Vec::new();
17    walk(tree.root_node(), source, &mut symbols);
18    Ok(symbols)
19}
20
21fn walk(node: Node, source: &[u8], out: &mut Vec<Symbol>) {
22    match node.kind() {
23        "function_item" => {
24            if let Some(sym) = named(node, source, SymbolKind::Function, "name") {
25                out.push(sym);
26            }
27        }
28        "struct_item" => {
29            if let Some(sym) = named(node, source, SymbolKind::Struct, "name") {
30                out.push(sym);
31            }
32        }
33        "enum_item" => {
34            if let Some(sym) = named(node, source, SymbolKind::Enum, "name") {
35                out.push(sym);
36            }
37        }
38        "trait_item" => {
39            if let Some(sym) = named(node, source, SymbolKind::Trait, "name") {
40                out.push(sym);
41            }
42        }
43        "impl_item" => {
44            let name = node
45                .child_by_field_name("type")
46                .and_then(|n| n.utf8_text(source).ok())
47                .unwrap_or("_")
48                .to_string();
49            out.push(Symbol {
50                name,
51                kind: SymbolKind::Impl,
52                start_line: node.start_position().row as u32 + 1,
53                end_line: node.end_position().row as u32 + 1,
54            });
55        }
56        _ => {}
57    }
58
59    let mut cursor = node.walk();
60    for child in node.children(&mut cursor) {
61        walk(child, source, out);
62    }
63}
64
65pub fn extract_imports(source: &[u8]) -> Result<Vec<ImportEdge>, crate::ParseError> {
66    let mut parser = tree_sitter::Parser::new();
67    parser
68        .set_language(&tree_sitter_rust::language())
69        .map_err(|e| crate::ParseError::Parse(e.to_string()))?;
70    let tree = parser
71        .parse(source, None)
72        .ok_or_else(|| crate::ParseError::Parse("tree-sitter returned None".into()))?;
73
74    let mut edges = Vec::new();
75    collect_use_decls(tree.root_node(), source, &mut edges);
76    Ok(edges)
77}
78
79fn collect_use_decls(node: Node, source: &[u8], out: &mut Vec<ImportEdge>) {
80    if node.kind() == "use_declaration" {
81        if let Ok(text) = node.utf8_text(source) {
82            // Strip leading "use " and trailing ";"
83            let raw = text
84                .trim()
85                .strip_prefix("use ")
86                .unwrap_or(text.trim())
87                .trim_end_matches(';')
88                .trim()
89                .to_string();
90            out.push(ImportEdge {
91                source_path: std::path::PathBuf::new(),
92                target_raw: raw,
93                resolved_path: None,
94                kind: ImportKind::Use,
95                line: node.start_position().row as u32 + 1,
96            });
97        }
98    }
99    let mut cursor = node.walk();
100    for child in node.children(&mut cursor) {
101        collect_use_decls(child, source, out);
102    }
103}
104
105fn named(node: Node, source: &[u8], kind: SymbolKind, field: &str) -> Option<Symbol> {
106    let name = node
107        .child_by_field_name(field)?
108        .utf8_text(source)
109        .ok()?
110        .to_string();
111    Some(Symbol {
112        name,
113        kind,
114        start_line: node.start_position().row as u32 + 1,
115        end_line: node.end_position().row as u32 + 1,
116    })
117}