Skip to main content

graphyn_core/
graph.rs

1use dashmap::DashMap;
2use petgraph::graph::{DiGraph, NodeIndex};
3
4use crate::ir::{Relationship, RelationshipKind, Symbol, SymbolId};
5use crate::resolver::AliasEntry;
6
7#[derive(Debug, Clone)]
8pub struct RelationshipMeta {
9    pub kind: RelationshipKind,
10    pub alias: Option<String>,
11    pub properties_accessed: Vec<String>,
12    pub context: String,
13    pub file: String,
14    pub line: u32,
15}
16
17#[derive(Debug)]
18pub struct GraphynGraph {
19    pub graph: DiGraph<SymbolId, RelationshipMeta>,
20    pub node_index: DashMap<SymbolId, NodeIndex>,
21    pub name_index: DashMap<String, Vec<SymbolId>>,
22    pub file_index: DashMap<String, Vec<SymbolId>>,
23    pub symbols: DashMap<SymbolId, Symbol>,
24    pub alias_chains: DashMap<SymbolId, Vec<AliasEntry>>,
25}
26
27impl Default for GraphynGraph {
28    fn default() -> Self {
29        Self::new()
30    }
31}
32
33impl GraphynGraph {
34    pub fn new() -> Self {
35        Self {
36            graph: DiGraph::new(),
37            node_index: DashMap::new(),
38            name_index: DashMap::new(),
39            file_index: DashMap::new(),
40            symbols: DashMap::new(),
41            alias_chains: DashMap::new(),
42        }
43    }
44
45    pub fn add_symbol(&mut self, symbol: Symbol) {
46        if self.node_index.contains_key(&symbol.id) {
47            self.replace_symbol(symbol);
48            return;
49        }
50
51        let symbol_id = symbol.id.clone();
52        let symbol_name = symbol.name.clone();
53        let file = symbol.file.clone();
54
55        let node = self.graph.add_node(symbol_id.clone());
56        self.node_index.insert(symbol_id.clone(), node);
57        self.symbols.insert(symbol_id.clone(), symbol);
58
59        self.name_index
60            .entry(symbol_name)
61            .and_modify(|ids| {
62                ids.push(symbol_id.clone());
63                ids.sort();
64                ids.dedup();
65            })
66            .or_insert_with(|| vec![symbol_id.clone()]);
67
68        self.file_index
69            .entry(file)
70            .and_modify(|ids| {
71                ids.push(symbol_id.clone());
72                ids.sort();
73                ids.dedup();
74            })
75            .or_insert_with(|| vec![symbol_id]);
76    }
77
78    pub fn replace_symbol(&mut self, symbol: Symbol) {
79        let symbol_id = symbol.id.clone();
80        if let Some(existing) = self.symbols.get(&symbol_id) {
81            let existing_name = existing.name.clone();
82            let existing_file = existing.file.clone();
83            drop(existing);
84
85            if existing_name != symbol.name {
86                if let Some(mut ids) = self.name_index.get_mut(&existing_name) {
87                    ids.retain(|id| id != &symbol_id);
88                }
89                self.name_index
90                    .entry(symbol.name.clone())
91                    .and_modify(|ids| {
92                        ids.push(symbol_id.clone());
93                        ids.sort();
94                        ids.dedup();
95                    })
96                    .or_insert_with(|| vec![symbol_id.clone()]);
97            }
98
99            if existing_file != symbol.file {
100                if let Some(mut ids) = self.file_index.get_mut(&existing_file) {
101                    ids.retain(|id| id != &symbol_id);
102                }
103                self.file_index
104                    .entry(symbol.file.clone())
105                    .and_modify(|ids| {
106                        ids.push(symbol_id.clone());
107                        ids.sort();
108                        ids.dedup();
109                    })
110                    .or_insert_with(|| vec![symbol_id.clone()]);
111            }
112        }
113
114        self.symbols.insert(symbol_id, symbol);
115    }
116
117    pub fn add_relationship(&mut self, relationship: &Relationship) {
118        let Some(from) = self.node_index.get(&relationship.from).map(|v| *v) else {
119            return;
120        };
121        let Some(to) = self.node_index.get(&relationship.to).map(|v| *v) else {
122            return;
123        };
124
125        let meta = RelationshipMeta {
126            kind: relationship.kind.clone(),
127            alias: relationship.alias.clone(),
128            properties_accessed: relationship.properties_accessed.clone(),
129            context: relationship.context.clone(),
130            file: relationship.file.clone(),
131            line: relationship.line,
132        };
133        self.graph.add_edge(from, to, meta);
134    }
135
136    pub fn remove_relationships_in_file(&mut self, file: &str) -> usize {
137        let edge_ids: Vec<_> = self
138            .graph
139            .edge_indices()
140            .filter(|edge_id| {
141                self.graph
142                    .edge_weight(*edge_id)
143                    .map(|meta| meta.file == file)
144                    .unwrap_or(false)
145            })
146            .collect();
147
148        let removed = edge_ids.len();
149        for edge_id in edge_ids {
150            let _ = self.graph.remove_edge(edge_id);
151        }
152        removed
153    }
154
155    pub fn remove_file(&mut self, file: &str) -> Vec<SymbolId> {
156        let mut removed = Vec::new();
157        let symbol_ids = self
158            .file_index
159            .remove(file)
160            .map(|(_, ids)| ids)
161            .unwrap_or_default();
162
163        for symbol_id in &symbol_ids {
164            if let Some((_, symbol)) = self.symbols.remove(symbol_id) {
165                if let Some(mut ids) = self.name_index.get_mut(&symbol.name) {
166                    ids.retain(|id| id != symbol_id);
167                }
168            }
169            if let Some((_, node)) = self.node_index.remove(symbol_id) {
170                let _ = self.graph.remove_node(node);
171            }
172            self.alias_chains.remove(symbol_id);
173            removed.push(symbol_id.clone());
174        }
175
176        self.rebuild_node_index();
177        removed.sort();
178        removed
179    }
180
181    fn rebuild_node_index(&self) {
182        self.node_index.clear();
183        for node_index in self.graph.node_indices() {
184            if let Some(symbol_id) = self.graph.node_weight(node_index) {
185                self.node_index.insert(symbol_id.clone(), node_index);
186            }
187        }
188    }
189}