codesynapse_core/ts_extract/
go.rs1use super::{add_contains_edge, add_node_if_missing, make_file_node, run_query_matches_ranged};
2use crate::error::Result;
3use crate::extract::{make_id, ImportNode, LanguageExtractor};
4use crate::types::{Edge, ExtractionFragment, Node};
5use std::collections::HashMap;
6use std::path::Path;
7
8pub struct TsGoExtractor;
9
10impl TsGoExtractor {
11 pub fn extract(source: &[u8], path: &Path) -> Result<ExtractionFragment> {
12 let (file_id, _, file_node) = make_file_node(path);
13 let mut fragment = ExtractionFragment {
14 nodes: vec![file_node],
15 edges: vec![],
16 };
17
18 let lang = tree_sitter_go::LANGUAGE.into();
19
20 let struct_query = r#"
22 (type_declaration
23 (type_spec
24 name: (type_identifier) @struct.name
25 type: (struct_type)
26 )
27 ) @struct.node
28 "#;
29 if let Ok(matches) = run_query_matches_ranged(source, &lang, struct_query) {
30 for (texts, ranges) in &matches {
31 let name = match texts.get("struct.name") {
32 Some(n) => n.clone(),
33 None => continue,
34 };
35 let source_location = ranges
36 .get("struct.node")
37 .map(|(s, e)| format!("{}:{}", s, e));
38 let struct_id = make_id(&[&file_id, &name]);
39 fragment.nodes.push(Node {
40 id: struct_id.clone(),
41 label: name.clone(),
42 file_type: "struct".to_string(),
43 source_file: path.to_string_lossy().to_string(),
44 source_location,
45 community: None,
46 rationale: None,
47 docstring: None,
48 metadata: {
49 let mut m = HashMap::new();
50 m.insert("kind".to_string(), "struct".to_string());
51 m
52 },
53 });
54 add_contains_edge(&mut fragment, &file_id, struct_id, path);
55 }
56 }
57
58 let fn_query = r#"
60 (function_declaration
61 name: (identifier) @fn.name
62 ) @fn.node
63 "#;
64 if let Ok(matches) = run_query_matches_ranged(source, &lang, fn_query) {
65 for (texts, ranges) in &matches {
66 let name = match texts.get("fn.name") {
67 Some(n) => n.clone(),
68 None => continue,
69 };
70 let source_location = ranges.get("fn.node").map(|(s, e)| format!("{}:{}", s, e));
71 let fn_id = make_id(&[&file_id, &name]);
72 let label = format!("{}()", name);
73 fragment.nodes.push(Node {
74 id: fn_id.clone(),
75 label,
76 file_type: "function".to_string(),
77 source_file: path.to_string_lossy().to_string(),
78 source_location,
79 community: None,
80 rationale: None,
81 docstring: None,
82 metadata: {
83 let mut m = HashMap::new();
84 m.insert("kind".to_string(), "function".to_string());
85 m
86 },
87 });
88 add_contains_edge(&mut fragment, &file_id, fn_id, path);
89 }
90 }
91
92 let ptr_recv_query = r#"
94 (method_declaration
95 receiver: (parameter_list
96 (parameter_declaration
97 type: (pointer_type (type_identifier) @recv.type)
98 )
99 )
100 name: (field_identifier) @fn.name
101 ) @fn.node
102 "#;
103 Self::extract_methods(source, &lang, path, &file_id, &mut fragment, ptr_recv_query);
104
105 let val_recv_query = r#"
107 (method_declaration
108 receiver: (parameter_list
109 (parameter_declaration
110 type: (type_identifier) @recv.type
111 )
112 )
113 name: (field_identifier) @fn.name
114 ) @fn.node
115 "#;
116 Self::extract_methods(source, &lang, path, &file_id, &mut fragment, val_recv_query);
117
118 Ok(fragment)
119 }
120
121 fn extract_methods(
122 source: &[u8],
123 lang: &tree_sitter::Language,
124 path: &Path,
125 file_id: &String,
126 fragment: &mut ExtractionFragment,
127 query_str: &str,
128 ) {
129 let Ok(matches) = run_query_matches_ranged(source, lang, query_str) else {
130 return;
131 };
132
133 let existing_struct_ids: std::collections::HashSet<String> =
135 fragment.nodes.iter().map(|n| n.id.clone()).collect();
136
137 for (texts, ranges) in &matches {
138 let fn_name = match texts.get("fn.name") {
139 Some(n) => n.clone(),
140 None => continue,
141 };
142 let recv_type = match texts.get("recv.type") {
143 Some(t) => t.clone(),
144 None => continue,
145 };
146 let source_location = ranges.get("fn.node").map(|(s, e)| format!("{}:{}", s, e));
147 let method_id = make_id(&[file_id, &recv_type, &fn_name]);
148 let label = format!("{}()", fn_name);
149
150 let method_node = Node {
151 id: method_id.clone(),
152 label,
153 file_type: "method".to_string(),
154 source_file: path.to_string_lossy().to_string(),
155 source_location,
156 community: None,
157 rationale: None,
158 docstring: None,
159 metadata: {
160 let mut m = HashMap::new();
161 m.insert("kind".to_string(), "method".to_string());
162 m
163 },
164 };
165 add_node_if_missing(fragment, method_node);
166
167 add_contains_edge(fragment, file_id, method_id.clone(), path);
169
170 let struct_id = make_id(&[file_id, &recv_type]);
172 if existing_struct_ids.contains(&struct_id) {
173 fragment.edges.push(Edge {
174 source: struct_id,
175 target: method_id,
176 relation: "contains".to_string(),
177 confidence: "EXTRACTED".to_string(),
178 source_file: Some(path.to_string_lossy().to_string()),
179 weight: 1.0,
180 context: None,
181 });
182 }
183 }
184 }
185}
186
187impl LanguageExtractor for TsGoExtractor {
189 fn file_extensions(&self) -> Vec<&'static str> {
190 vec!["go"]
191 }
192 fn extract(&self, source: &[u8], path: &Path) -> Result<ExtractionFragment> {
193 Self::extract(source, path)
194 }
195 fn resolve_imports(&self, _imports: &[ImportNode]) -> Vec<Edge> {
196 vec![]
197 }
198 fn collect_type_refs(&self, _fragment: &mut ExtractionFragment) {}
199}