Skip to main content

weavatrix_graph/working/
mutate.rs

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