1use std::path::Path;
6
7use crate::types::{AcbResult, CodeUnitType, Language, Visibility};
8
9use super::treesitter::{get_node_text, node_to_span};
10use super::{LanguageParser, RawCodeUnit};
11
12pub struct GoParser;
14
15impl Default for GoParser {
16 fn default() -> Self {
17 Self::new()
18 }
19}
20
21impl GoParser {
22 pub fn new() -> Self {
24 Self
25 }
26
27 fn extract_from_node(
28 &self,
29 node: tree_sitter::Node,
30 source: &str,
31 file_path: &Path,
32 units: &mut Vec<RawCodeUnit>,
33 next_id: &mut u64,
34 parent_qname: &str,
35 ) {
36 let mut cursor = node.walk();
37 for child in node.children(&mut cursor) {
38 match child.kind() {
39 "function_declaration" => {
40 if let Some(unit) =
41 self.extract_function(child, source, file_path, parent_qname, next_id)
42 {
43 units.push(unit);
44 }
45 }
46 "method_declaration" => {
47 if let Some(unit) =
48 self.extract_method(child, source, file_path, parent_qname, next_id)
49 {
50 units.push(unit);
51 }
52 }
53 "type_declaration" => {
54 self.extract_type_decl(child, source, file_path, units, next_id, parent_qname);
55 }
56 "import_declaration" => {
57 if let Some(unit) =
58 self.extract_import(child, source, file_path, parent_qname, next_id)
59 {
60 units.push(unit);
61 }
62 }
63 _ => {}
64 }
65 }
66 }
67
68 fn extract_function(
69 &self,
70 node: tree_sitter::Node,
71 source: &str,
72 file_path: &Path,
73 parent_qname: &str,
74 next_id: &mut u64,
75 ) -> Option<RawCodeUnit> {
76 let name_node = node.child_by_field_name("name")?;
77 let name = get_node_text(name_node, source).to_string();
78 let qname = go_qname(parent_qname, &name);
79 let span = node_to_span(node);
80
81 let vis = if name
82 .chars()
83 .next()
84 .map(|c| c.is_uppercase())
85 .unwrap_or(false)
86 {
87 Visibility::Public
88 } else {
89 Visibility::Private
90 };
91
92 let is_test = name.starts_with("Test") || name.starts_with("Benchmark");
93
94 let id = *next_id;
95 *next_id += 1;
96
97 let unit_type = if is_test {
98 CodeUnitType::Test
99 } else {
100 CodeUnitType::Function
101 };
102 let mut unit =
103 RawCodeUnit::new(unit_type, Language::Go, name, file_path.to_path_buf(), span);
104 unit.temp_id = id;
105 unit.qualified_name = qname;
106 unit.visibility = vis;
107
108 Some(unit)
109 }
110
111 fn extract_method(
112 &self,
113 node: tree_sitter::Node,
114 source: &str,
115 file_path: &Path,
116 parent_qname: &str,
117 next_id: &mut u64,
118 ) -> Option<RawCodeUnit> {
119 let name_node = node.child_by_field_name("name")?;
120 let name = get_node_text(name_node, source).to_string();
121 let qname = go_qname(parent_qname, &name);
122 let span = node_to_span(node);
123
124 let vis = if name
125 .chars()
126 .next()
127 .map(|c| c.is_uppercase())
128 .unwrap_or(false)
129 {
130 Visibility::Public
131 } else {
132 Visibility::Private
133 };
134
135 let id = *next_id;
136 *next_id += 1;
137
138 let mut unit = RawCodeUnit::new(
139 CodeUnitType::Function,
140 Language::Go,
141 name,
142 file_path.to_path_buf(),
143 span,
144 );
145 unit.temp_id = id;
146 unit.qualified_name = qname;
147 unit.visibility = vis;
148
149 Some(unit)
150 }
151
152 fn extract_type_decl(
153 &self,
154 node: tree_sitter::Node,
155 source: &str,
156 file_path: &Path,
157 units: &mut Vec<RawCodeUnit>,
158 next_id: &mut u64,
159 parent_qname: &str,
160 ) {
161 let mut cursor = node.walk();
162 for child in node.children(&mut cursor) {
163 if child.kind() == "type_spec" {
164 if let Some(name_node) = child.child_by_field_name("name") {
165 let name = get_node_text(name_node, source).to_string();
166 let qname = go_qname(parent_qname, &name);
167 let span = node_to_span(child);
168
169 let vis = if name
170 .chars()
171 .next()
172 .map(|c| c.is_uppercase())
173 .unwrap_or(false)
174 {
175 Visibility::Public
176 } else {
177 Visibility::Private
178 };
179
180 let id = *next_id;
181 *next_id += 1;
182
183 let type_def = child.child_by_field_name("type");
185 let unit_type = if type_def
186 .map(|t| t.kind() == "interface_type")
187 .unwrap_or(false)
188 {
189 CodeUnitType::Trait
190 } else {
191 CodeUnitType::Type
192 };
193
194 let mut unit = RawCodeUnit::new(
195 unit_type,
196 Language::Go,
197 name,
198 file_path.to_path_buf(),
199 span,
200 );
201 unit.temp_id = id;
202 unit.qualified_name = qname;
203 unit.visibility = vis;
204 units.push(unit);
205 }
206 }
207 }
208 }
209
210 fn extract_import(
211 &self,
212 node: tree_sitter::Node,
213 source: &str,
214 file_path: &Path,
215 parent_qname: &str,
216 next_id: &mut u64,
217 ) -> Option<RawCodeUnit> {
218 let _text = get_node_text(node, source);
219 let span = node_to_span(node);
220
221 let id = *next_id;
222 *next_id += 1;
223
224 let mut unit = RawCodeUnit::new(
225 CodeUnitType::Import,
226 Language::Go,
227 "import".to_string(),
228 file_path.to_path_buf(),
229 span,
230 );
231 unit.temp_id = id;
232 unit.qualified_name = go_qname(parent_qname, "import");
233
234 Some(unit)
235 }
236}
237
238impl LanguageParser for GoParser {
239 fn extract_units(
240 &self,
241 tree: &tree_sitter::Tree,
242 source: &str,
243 file_path: &Path,
244 ) -> AcbResult<Vec<RawCodeUnit>> {
245 let mut units = Vec::new();
246 let mut next_id = 0u64;
247
248 let module_name = file_path
249 .file_stem()
250 .and_then(|s| s.to_str())
251 .unwrap_or("unknown")
252 .to_string();
253
254 let root_span = node_to_span(tree.root_node());
255 let mut module_unit = RawCodeUnit::new(
256 CodeUnitType::Module,
257 Language::Go,
258 module_name.clone(),
259 file_path.to_path_buf(),
260 root_span,
261 );
262 module_unit.temp_id = next_id;
263 module_unit.qualified_name = module_name.clone();
264 next_id += 1;
265 units.push(module_unit);
266
267 self.extract_from_node(
268 tree.root_node(),
269 source,
270 file_path,
271 &mut units,
272 &mut next_id,
273 &module_name,
274 );
275
276 Ok(units)
277 }
278
279 fn is_test_file(&self, path: &Path, _source: &str) -> bool {
280 path.file_name()
281 .and_then(|n| n.to_str())
282 .map(|n| n.ends_with("_test.go"))
283 .unwrap_or(false)
284 }
285}
286
287fn go_qname(parent: &str, name: &str) -> String {
288 if parent.is_empty() {
289 name.to_string()
290 } else {
291 format!("{}.{}", parent, name)
292 }
293}