Skip to main content

codesynapse_core/ts_extract/
groovy.rs

1use super::{add_contains_edge, add_node_if_missing, make_file_node, run_query_named};
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 TsGroovyExtractor;
9
10impl TsGroovyExtractor {
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_groovy::LANGUAGE.into();
19
20        let class_query = r#"
21            (class_declaration
22                name: (identifier) @class.name
23            )
24        "#;
25        if let Ok(mut captures) = run_query_named(source, &lang, class_query) {
26            for name in captures.remove("class.name").unwrap_or_default() {
27                let id = make_id(&[&file_id, &name]);
28                fragment.nodes.push(Node {
29                    id: id.clone(),
30                    label: name,
31                    file_type: "class".to_string(),
32                    source_file: path.to_string_lossy().to_string(),
33                    source_location: None,
34                    community: None,
35                    rationale: None,
36                    docstring: None,
37                    metadata: HashMap::new(),
38                });
39                add_contains_edge(&mut fragment, &file_id, id, path);
40            }
41        }
42
43        let method_query = r#"
44            (method_declaration
45                name: (identifier) @method.name
46            )
47        "#;
48        if let Ok(mut captures) = run_query_named(source, &lang, method_query) {
49            for name in captures.remove("method.name").unwrap_or_default() {
50                let label = format!("{}()", name);
51                let id = make_id(&[&file_id, &name]);
52                fragment.nodes.push(Node {
53                    id: id.clone(),
54                    label,
55                    file_type: "method".to_string(),
56                    source_file: path.to_string_lossy().to_string(),
57                    source_location: None,
58                    community: None,
59                    rationale: None,
60                    docstring: None,
61                    metadata: HashMap::new(),
62                });
63                add_contains_edge(&mut fragment, &file_id, id, path);
64            }
65        }
66
67        // tree-sitter-groovy 0.1.2 import_declaration has no named fields; text scan is reliable
68        let source_str = std::str::from_utf8(source).unwrap_or("");
69        for line in source_str.lines() {
70            let trimmed = line.trim();
71            if !trimmed.starts_with("import ") {
72                continue;
73            }
74            let path_part = trimmed[7..].trim().trim_end_matches(';');
75            let name = path_part.split('.').next_back().unwrap_or(path_part).trim();
76            if name.is_empty() || name == "*" {
77                continue;
78            }
79            let name = name.to_string();
80            let mod_id = make_id(&[&file_id, &name]);
81            add_node_if_missing(
82                &mut fragment,
83                Node {
84                    id: mod_id.clone(),
85                    label: name,
86                    file_type: "module".to_string(),
87                    source_file: path.to_string_lossy().to_string(),
88                    source_location: None,
89                    community: None,
90                    rationale: None,
91                    docstring: None,
92                    metadata: HashMap::new(),
93                },
94            );
95            fragment.edges.push(Edge {
96                source: file_id.clone(),
97                target: mod_id,
98                relation: "imports".to_string(),
99                confidence: "EXTRACTED".to_string(),
100                source_file: Some(path.to_string_lossy().to_string()),
101                weight: 1.0,
102                context: Some("import".to_string()),
103            });
104        }
105
106        Ok(fragment)
107    }
108}
109
110impl LanguageExtractor for TsGroovyExtractor {
111    fn file_extensions(&self) -> Vec<&'static str> {
112        vec!["groovy", "gvy", "gy", "gsh"]
113    }
114    fn extract(&self, source: &[u8], path: &Path) -> Result<ExtractionFragment> {
115        Self::extract(source, path)
116    }
117    fn resolve_imports(&self, _imports: &[ImportNode]) -> Vec<Edge> {
118        vec![]
119    }
120    fn collect_type_refs(&self, _fragment: &mut ExtractionFragment) {}
121}