codesynapse_core/ts_extract/
bash.rs1use 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 TsBashExtractor;
9
10impl TsBashExtractor {
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_bash::LANGUAGE.into();
19 let content = std::str::from_utf8(source).unwrap_or("");
20
21 let fn_query = r#"
23 (function_definition
24 name: (word) @func.name
25 body: (_)
26 )
27 "#;
28 if let Ok(mut captures) = run_query_named(source, &lang, fn_query) {
29 let names = captures.remove("func.name").unwrap_or_default();
30 for name in &names {
31 let fn_id = make_id(&[&file_id, name, "()"]);
32 fragment.nodes.push(Node {
33 id: fn_id.clone(),
34 label: format!("{}()", name),
35 file_type: "function".to_string(),
36 source_file: path.to_string_lossy().to_string(),
37 source_location: None,
38 community: None,
39 rationale: None,
40 docstring: None,
41 metadata: HashMap::new(),
42 });
43 add_contains_edge(&mut fragment, &file_id, fn_id, path);
44 }
45 }
46
47 for line in content.lines() {
49 let trimmed = line.trim();
50 if trimmed.starts_with("source ") || trimmed.starts_with(". ") {
51 let target = trimmed
52 .strip_prefix("source ")
53 .or_else(|| trimmed.strip_prefix(". "))
54 .unwrap_or("")
55 .trim()
56 .trim_matches('"')
57 .trim_matches('\'');
58 if !target.is_empty() {
59 let source_id = make_id(&[&file_id, target]);
60 let source_node = Node {
61 id: source_id.clone(),
62 label: target.to_string(),
63 file_type: "code".to_string(),
64 source_file: path.to_string_lossy().to_string(),
65 source_location: None,
66 community: None,
67 rationale: None,
68 docstring: None,
69 metadata: HashMap::new(),
70 };
71 add_node_if_missing(&mut fragment, source_node);
72 fragment.edges.push(Edge {
73 source: file_id.clone(),
74 target: source_id,
75 relation: "imports_from".to_string(),
76 confidence: "EXTRACTED".to_string(),
77 source_file: Some(path.to_string_lossy().to_string()),
78 weight: 1.0,
79 context: None,
80 });
81 }
82 }
83 }
84
85 Ok(fragment)
86 }
87}
88
89impl LanguageExtractor for TsBashExtractor {
91 fn file_extensions(&self) -> Vec<&'static str> {
92 vec!["sh", "bash"]
93 }
94 fn extract(&self, source: &[u8], path: &Path) -> Result<ExtractionFragment> {
95 Self::extract(source, path)
96 }
97 fn resolve_imports(&self, _imports: &[ImportNode]) -> Vec<Edge> {
98 vec![]
99 }
100 fn collect_type_refs(&self, _fragment: &mut ExtractionFragment) {}
101}