llmcc_core/
block_rel.rs

1use std::collections::{HashMap, HashSet};
2use std::sync::RwLock;
3
4use crate::block::{BlockId, BlockRelation};
5
6/// Manages relationships between blocks in a clean, type-safe way
7#[derive(Debug, Default)]
8pub struct BlockRelationMap {
9    /// BlockId -> (Relation -> Vec<BlockId>)
10    relations: RwLock<HashMap<BlockId, HashMap<BlockRelation, Vec<BlockId>>>>,
11}
12
13impl Clone for BlockRelationMap {
14    fn clone(&self) -> Self {
15        let snapshot = self.relations.read().unwrap().clone();
16        Self {
17            relations: RwLock::new(snapshot),
18        }
19    }
20}
21
22impl BlockRelationMap {
23    /// Add a relationship between two blocks
24    pub fn add_relation_impl(&self, from: BlockId, relation: BlockRelation, to: BlockId) {
25        let mut relations = self.relations.write().unwrap();
26        relations
27            .entry(from)
28            .or_default()
29            .entry(relation)
30            .or_default()
31            .push(to);
32    }
33
34    /// Add multiple relationships of the same type from one block
35    pub fn add_relation_impls(&self, from: BlockId, relation: BlockRelation, targets: &[BlockId]) {
36        let mut relations = self.relations.write().unwrap();
37        let block_relations = relations.entry(from).or_default();
38        let relation_vec = block_relations.entry(relation).or_default();
39        relation_vec.extend_from_slice(targets);
40    }
41
42    /// Remove a specific relationship
43    pub fn remove_relation_impl(
44        &self,
45        from: BlockId,
46        relation: BlockRelation,
47        to: BlockId,
48    ) -> bool {
49        let mut relations = self.relations.write().unwrap();
50        if let Some(block_relations) = relations.get_mut(&from) {
51            if let Some(targets) = block_relations.get_mut(&relation) {
52                if let Some(pos) = targets.iter().position(|&x| x == to) {
53                    targets.remove(pos);
54                    // Clean up empty vectors
55                    if targets.is_empty() {
56                        block_relations.remove(&relation);
57                        // Clean up empty maps
58                        if block_relations.is_empty() {
59                            relations.remove(&from);
60                        }
61                    }
62                    return true;
63                }
64            }
65        }
66        false
67    }
68
69    /// Remove all relationships of a specific type from a block
70    pub fn remove_all_relations(&self, from: BlockId, relation: BlockRelation) -> Vec<BlockId> {
71        let mut relations = self.relations.write().unwrap();
72        if let Some(block_relations) = relations.get_mut(&from) {
73            if let Some(targets) = block_relations.remove(&relation) {
74                // Clean up empty maps
75                if block_relations.is_empty() {
76                    relations.remove(&from);
77                }
78                return targets;
79            }
80        }
81        Vec::new()
82    }
83
84    /// Remove all relationships for a block (useful when deleting a block)
85    pub fn remove_block_relations(&self, block_id: BlockId) {
86        let mut relations = self.relations.write().unwrap();
87        relations.remove(&block_id);
88    }
89
90    /// Get all blocks related to a given block with a specific relationship
91    pub fn get_related(&self, from: BlockId, relation: BlockRelation) -> Vec<BlockId> {
92        self.relations
93            .read()
94            .unwrap()
95            .get(&from)
96            .and_then(|block_relations| block_relations.get(&relation))
97            .cloned()
98            .unwrap_or_default()
99    }
100
101    /// Get all relationships for a specific block
102    pub fn get_all_relations(&self, from: BlockId) -> HashMap<BlockRelation, Vec<BlockId>> {
103        self.relations
104            .read()
105            .unwrap()
106            .get(&from)
107            .cloned()
108            .unwrap_or_default()
109    }
110
111    /// Check if a specific relationship exists
112    pub fn has_relation(&self, from: BlockId, relation: BlockRelation, to: BlockId) -> bool {
113        self.relations
114            .read()
115            .unwrap()
116            .get(&from)
117            .and_then(|block_relations| block_relations.get(&relation))
118            .map(|targets| targets.contains(&to))
119            .unwrap_or(false)
120    }
121
122    /// Add a relation if it doesn't already exist (optimized: single borrow)
123    pub fn add_relation_if_not_exists(&self, from: BlockId, relation: BlockRelation, to: BlockId) {
124        let mut relations = self.relations.write().unwrap();
125        let block_relations = relations.entry(from).or_default();
126        let targets = block_relations.entry(relation).or_default();
127        if !targets.contains(&to) {
128            targets.push(to);
129        }
130    }
131
132    /// Add bidirectional relation if it doesn't already exist (optimized: single borrow)
133    pub fn add_bidirectional_if_not_exists(&self, caller: BlockId, callee: BlockId) {
134        let mut relations = self.relations.write().unwrap();
135
136        // Add caller -> callee (DependsOn)
137        let caller_relations = relations.entry(caller).or_default();
138        let caller_targets = caller_relations
139            .entry(BlockRelation::DependsOn)
140            .or_default();
141        if !caller_targets.contains(&callee) {
142            caller_targets.push(callee);
143        }
144
145        // Add callee -> caller (DependedBy)
146        let callee_relations = relations.entry(callee).or_default();
147        let callee_targets = callee_relations
148            .entry(BlockRelation::DependedBy)
149            .or_default();
150        if !callee_targets.contains(&caller) {
151            callee_targets.push(caller);
152        }
153    }
154
155    /// Check if any relationship of a type exists
156    pub fn has_relation_type(&self, from: BlockId, relation: BlockRelation) -> bool {
157        self.relations
158            .read()
159            .unwrap()
160            .get(&from)
161            .and_then(|block_relations| block_relations.get(&relation))
162            .map(|targets| !targets.is_empty())
163            .unwrap_or(false)
164    }
165
166    /// Get all blocks that have any relationships
167    pub fn get_connected_blocks(&self) -> Vec<BlockId> {
168        self.relations.read().unwrap().keys().copied().collect()
169    }
170
171    /// Get all blocks related to a given block (regardless of relationship type)
172    pub fn get_all_related_blocks(&self, from: BlockId) -> HashSet<BlockId> {
173        let mut result = HashSet::new();
174        if let Some(block_relations) = self.relations.read().unwrap().get(&from) {
175            for targets in block_relations.values() {
176                result.extend(targets.iter().copied());
177            }
178        }
179        result
180    }
181
182    /// Find all blocks that point to a given block with a specific relationship
183    pub fn find_reverse_relations(&self, to: BlockId, relation: BlockRelation) -> Vec<BlockId> {
184        let mut result = Vec::new();
185        let relations = self.relations.read().unwrap();
186
187        for (&from_block, block_relations) in relations.iter() {
188            if let Some(targets) = block_relations.get(&relation) {
189                if targets.contains(&to) {
190                    result.push(from_block);
191                }
192            }
193        }
194        result
195    }
196
197    /// Get statistics about relationships
198    pub fn stats(&self) -> RelationStats {
199        let relations = self.relations.read().unwrap();
200        let mut total_relations = 0;
201        let mut by_relation: HashMap<BlockRelation, usize> = HashMap::new();
202
203        for block_relations in relations.values() {
204            for (&relation, targets) in block_relations.iter() {
205                by_relation
206                    .entry(relation)
207                    .and_modify(|count| *count += targets.len())
208                    .or_insert_with(|| targets.len());
209                total_relations += targets.len();
210            }
211        }
212
213        RelationStats {
214            total_blocks: relations.len(),
215            total_relations,
216            by_relation,
217        }
218    }
219
220    /// Clear all relationships
221    pub fn clear(&self) {
222        self.relations.write().unwrap().clear();
223    }
224
225    /// Check if the map is empty
226    pub fn is_empty(&self) -> bool {
227        self.relations.read().unwrap().is_empty()
228    }
229
230    /// Get the number of blocks with relationships
231    pub fn len(&self) -> usize {
232        self.relations.read().unwrap().len()
233    }
234}
235
236/// Helper struct for building relationships fluently
237pub struct RelationBuilder<'a> {
238    map: &'a BlockRelationMap,
239    from: BlockId,
240}
241
242impl<'a> RelationBuilder<'a> {
243    fn new(map: &'a BlockRelationMap, from: BlockId) -> Self {
244        Self { map, from }
245    }
246
247    /// Add a "calls" relationship
248    pub fn calls(self, to: BlockId) -> Self {
249        self.map
250            .add_relation_impl(self.from, BlockRelation::DependsOn, to);
251        self
252    }
253
254    /// Add a "called by" relationship
255    pub fn called_by(self, to: BlockId) -> Self {
256        self.map
257            .add_relation_impl(self.from, BlockRelation::DependedBy, to);
258        self
259    }
260
261    /// Add a "contains" relationship
262    pub fn contains(self, to: BlockId) -> Self {
263        self.map
264            .add_relation_impl(self.from, BlockRelation::Unknown, to);
265        self
266    }
267
268    /// Add a "contained by" relationship
269    pub fn contained_by(self, to: BlockId) -> Self {
270        self.map
271            .add_relation_impl(self.from, BlockRelation::Unknown, to);
272        self
273    }
274
275    /// Add a custom relationship
276    pub fn relation(self, relation: BlockRelation, to: BlockId) -> Self {
277        self.map.add_relation_impl(self.from, relation, to);
278        self
279    }
280
281    /// Add multiple relationships of the same type
282    pub fn relations(self, relation: BlockRelation, targets: &[BlockId]) -> Self {
283        self.map.add_relation_impls(self.from, relation, targets);
284        self
285    }
286}
287
288impl BlockRelationMap {
289    /// Create a fluent builder for adding relationships from a block
290    pub fn from_block(&self, from: BlockId) -> RelationBuilder<'_> {
291        RelationBuilder::new(self, from)
292    }
293}
294
295/// Statistics about relationships in the map
296#[derive(Debug, Default, Clone)]
297pub struct RelationStats {
298    pub total_blocks: usize,
299    pub total_relations: usize,
300    pub by_relation: HashMap<BlockRelation, usize>,
301}
302
303impl std::fmt::Display for RelationStats {
304    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
305        writeln!(f, "Relation Stats:")?;
306        writeln!(f, "  Total blocks with relations: {}", self.total_blocks)?;
307        writeln!(f, "  Total relationships: {}", self.total_relations)?;
308        writeln!(f, "  By type:")?;
309        for (&relation, &count) in &self.by_relation {
310            writeln!(f, "    {}: {}", relation, count)?;
311        }
312        Ok(())
313    }
314}
315
316// Convenience functions for common relationship patterns
317impl BlockRelationMap {
318    /// Create a bidirectional call relationship
319    pub fn add_relation(&self, caller: BlockId, callee: BlockId) {
320        self.add_relation_impl(caller, BlockRelation::DependsOn, callee);
321        self.add_relation_impl(callee, BlockRelation::DependedBy, caller);
322    }
323
324    /// Remove a bidirectional call relationship
325    pub fn remove_relation(&self, caller: BlockId, callee: BlockId) {
326        self.remove_relation_impl(caller, BlockRelation::DependsOn, callee);
327        self.remove_relation_impl(callee, BlockRelation::DependedBy, caller);
328    }
329
330    pub fn get_depended(&self, block: BlockId) -> Vec<BlockId> {
331        self.get_related(block, BlockRelation::DependedBy)
332    }
333
334    pub fn get_depends(&self, block: BlockId) -> Vec<BlockId> {
335        self.get_related(block, BlockRelation::DependsOn)
336    }
337
338    /// Get all children of a block
339    pub fn get_children(&self, block: BlockId) -> Vec<BlockId> {
340        self.get_related(block, BlockRelation::Unknown)
341    }
342
343    /// Get the parent of a block (assumes single parent)
344    pub fn get_parent(&self, block: BlockId) -> Option<BlockId> {
345        self.find_reverse_relations(block, BlockRelation::Unknown)
346            .into_iter()
347            .next()
348    }
349
350    /// Get all ancestors of a block (walking up the containment hierarchy)
351    pub fn get_ancestors(&self, mut block: BlockId) -> Vec<BlockId> {
352        let mut ancestors = Vec::new();
353        let mut visited = HashSet::new();
354
355        while let Some(parent) = self.get_parent(block) {
356            if visited.contains(&parent) {
357                // Cycle detection
358                break;
359            }
360            visited.insert(parent);
361            ancestors.push(parent);
362            block = parent;
363        }
364
365        ancestors
366    }
367
368    /// Get all descendants of a block (walking down the containment hierarchy)
369    pub fn get_descendants(&self, block: BlockId) -> Vec<BlockId> {
370        let mut descendants = Vec::new();
371        let mut visited = HashSet::new();
372        let mut queue = vec![block];
373
374        while let Some(current) = queue.pop() {
375            if visited.contains(&current) {
376                continue;
377            }
378            visited.insert(current);
379
380            let children = self.get_children(current);
381            descendants.extend(&children);
382            queue.extend(children);
383        }
384
385        descendants
386    }
387}