Skip to main content

codesynapse_core/ts_extract/
bash.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 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        // Function definitions
22        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        // Source/import commands
48        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
89/// Vue SFC extractor (script extraction + template analysis)
90impl 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}