llmcc_core/
block_rel.rs

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