Skip to main content

weavatrix_graph/working/
mutate.rs

1use super::{StableEdgeKey, StableNodeKey, WorkingGraph};
2use crate::graph::validate::{validate_edge, validate_node};
3use crate::{Edge, GraphError, Node, Result};
4
5impl WorkingGraph {
6    pub fn remove_edge(&mut self, key: StableEdgeKey) -> Option<Edge> {
7        let endpoints = self.edge_endpoints(key)?;
8        let edge = self.edge_slot_mut(key)?.value.take()?.value;
9        self.node_slot_mut(endpoints.source())?
10            .outgoing
11            .retain(|candidate| *candidate != key);
12        self.node_slot_mut(endpoints.target())?
13            .incoming
14            .retain(|candidate| *candidate != key);
15        self.edge_count -= 1;
16        self.retire_edge_slot(key);
17        Some(edge)
18    }
19
20    pub fn remove_node(&mut self, key: StableNodeKey) -> Option<Node> {
21        let slot = self.node_slot(key)?;
22        let id = slot.value.as_ref()?.id.clone();
23        let mut incident = slot.outgoing.clone();
24        incident.extend(slot.incoming.iter().copied());
25        incident.sort_unstable();
26        incident.dedup();
27        for edge in incident {
28            self.remove_edge(edge);
29        }
30
31        let node = self.node_slot_mut(key)?.value.take()?;
32        self.node_by_id.remove(&id);
33        self.node_count -= 1;
34        self.retire_node_slot(key);
35        Some(node)
36    }
37
38    /// Replaces a node while retaining its stable key.
39    ///
40    /// # Errors
41    ///
42    /// Returns an error for invalid data or a conflicting new identifier.
43    pub fn replace_node(&mut self, key: StableNodeKey, node: Node) -> Result<Option<Node>> {
44        validate_node(&node)?;
45        let Some(current) = self.node(key) else {
46            return Ok(None);
47        };
48        let old_id = current.id.clone();
49        if self
50            .node_by_id
51            .get(&node.id)
52            .is_some_and(|existing| *existing != key)
53        {
54            return Err(GraphError::ConflictingNode {
55                id: node.id.to_string(),
56            });
57        }
58
59        if old_id != node.id {
60            let new_id = node.id.clone();
61            let Some(incident) = self.node_slot(key).map(|slot| {
62                let mut keys = slot.outgoing.clone();
63                keys.extend(slot.incoming.iter().copied());
64                keys.sort_unstable();
65                keys.dedup();
66                keys
67            }) else {
68                return Ok(None);
69            };
70            for edge in incident {
71                if let Some(working) = self
72                    .edge_slot_mut(edge)
73                    .and_then(|slot| slot.value.as_mut())
74                {
75                    if working.source == key {
76                        working.value.source = new_id.clone();
77                    }
78                    if working.target == key {
79                        working.value.target = new_id.clone();
80                    }
81                }
82            }
83            self.node_by_id.remove(&old_id);
84            self.node_by_id.insert(new_id, key);
85        }
86        Ok(self
87            .node_slot_mut(key)
88            .and_then(|slot| slot.value.replace(node)))
89    }
90
91    /// Replaces an edge while retaining its stable key.
92    ///
93    /// # Errors
94    ///
95    /// Returns an error for invalid evidence or missing new endpoints.
96    pub fn replace_edge(&mut self, key: StableEdgeKey, edge: Edge) -> Result<Option<Edge>> {
97        validate_edge(&edge)?;
98        let Some(old_endpoints) = self.edge_endpoints(key) else {
99            return Ok(None);
100        };
101        let source =
102            self.node_key(edge.source.as_str())
103                .ok_or_else(|| GraphError::MissingEdgeSource {
104                    id: edge.source.to_string(),
105                })?;
106        let target =
107            self.node_key(edge.target.as_str())
108                .ok_or_else(|| GraphError::MissingEdgeTarget {
109                    id: edge.target.to_string(),
110                })?;
111
112        if let Some(slot) = self.node_slot_mut(old_endpoints.source()) {
113            slot.outgoing.retain(|candidate| *candidate != key);
114        }
115        if let Some(slot) = self.node_slot_mut(old_endpoints.target()) {
116            slot.incoming.retain(|candidate| *candidate != key);
117        }
118        if let Some(slot) = self.node_slot_mut(source) {
119            slot.outgoing.push(key);
120        }
121        if let Some(slot) = self.node_slot_mut(target) {
122            slot.incoming.push(key);
123        }
124
125        let Some(working) = self.edge_slot_mut(key).and_then(|slot| slot.value.as_mut()) else {
126            return Ok(None);
127        };
128        working.source = source;
129        working.target = target;
130        Ok(Some(std::mem::replace(&mut working.value, edge)))
131    }
132
133    fn retire_node_slot(&mut self, key: StableNodeKey) {
134        let slot = &mut self.nodes[key.index()];
135        slot.outgoing.clear();
136        slot.incoming.clear();
137        if let Some(generation) = slot.generation.checked_add(1) {
138            slot.generation = generation;
139            self.free_nodes.push(key.slot());
140        }
141    }
142
143    fn retire_edge_slot(&mut self, key: StableEdgeKey) {
144        let slot = &mut self.edges[key.index()];
145        if let Some(generation) = slot.generation.checked_add(1) {
146            slot.generation = generation;
147            self.free_edges.push(key.slot());
148        }
149    }
150}