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 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}