weavatrix_graph/working/
core.rs1use super::{StableEdgeKey, StableNodeKey};
2use crate::graph::validate::{validate_edge, validate_node};
3use crate::{Edge, EdgeEndpoints, GraphError, Node, NodeId, Result};
4use std::collections::HashMap;
5
6pub(super) struct WorkingEdge {
7 pub(super) value: Edge,
8 pub(super) source: StableNodeKey,
9 pub(super) target: StableNodeKey,
10}
11
12pub(super) struct NodeSlot {
13 pub(super) generation: u32,
14 pub(super) value: Option<Node>,
15 pub(super) outgoing: Vec<StableEdgeKey>,
16 pub(super) incoming: Vec<StableEdgeKey>,
17}
18
19pub(super) struct EdgeSlot {
20 pub(super) generation: u32,
21 pub(super) value: Option<WorkingEdge>,
22}
23
24#[derive(Default)]
25pub struct WorkingGraph {
26 pub(super) nodes: Vec<NodeSlot>,
27 pub(super) edges: Vec<EdgeSlot>,
28 pub(super) node_by_id: HashMap<NodeId, StableNodeKey>,
29 pub(super) free_nodes: Vec<u32>,
30 pub(super) free_edges: Vec<u32>,
31 pub(super) node_count: usize,
32 pub(super) edge_count: usize,
33}
34
35impl WorkingGraph {
36 #[must_use]
37 pub fn new() -> Self {
38 Self::default()
39 }
40
41 #[must_use]
42 pub fn with_capacity(nodes: usize, edges: usize) -> Self {
43 Self {
44 nodes: Vec::with_capacity(nodes),
45 edges: Vec::with_capacity(edges),
46 node_by_id: HashMap::with_capacity(nodes),
47 free_nodes: Vec::new(),
48 free_edges: Vec::new(),
49 node_count: 0,
50 edge_count: 0,
51 }
52 }
53
54 pub fn insert_node(&mut self, node: Node) -> Result<StableNodeKey> {
60 validate_node(&node)?;
61 if let Some(&key) = self.node_by_id.get(&node.id) {
62 if self.node(key) == Some(&node) {
63 return Ok(key);
64 }
65 return Err(GraphError::ConflictingNode {
66 id: node.id.to_string(),
67 });
68 }
69 let id = node.id.clone();
70 let key = self.allocate_node(node)?;
71 self.node_by_id.insert(id, key);
72 self.node_count += 1;
73 Ok(key)
74 }
75
76 pub fn insert_edge(&mut self, edge: Edge) -> Result<StableEdgeKey> {
82 validate_edge(&edge)?;
83 let source = self.node_by_id.get(&edge.source).copied().ok_or_else(|| {
84 GraphError::MissingEdgeSource {
85 id: edge.source.to_string(),
86 }
87 })?;
88 let target = self.node_by_id.get(&edge.target).copied().ok_or_else(|| {
89 GraphError::MissingEdgeTarget {
90 id: edge.target.to_string(),
91 }
92 })?;
93 if self.node_slot(source).is_none() {
94 return Err(GraphError::MissingEdgeSource {
95 id: edge.source.to_string(),
96 });
97 }
98 if self.node_slot(target).is_none() {
99 return Err(GraphError::MissingEdgeTarget {
100 id: edge.target.to_string(),
101 });
102 }
103 let key = self.allocate_edge(WorkingEdge {
104 value: edge,
105 source,
106 target,
107 })?;
108 if let Some(slot) = self.node_slot_mut(source) {
109 slot.outgoing.push(key);
110 }
111 if let Some(slot) = self.node_slot_mut(target) {
112 slot.incoming.push(key);
113 }
114 self.edge_count += 1;
115 Ok(key)
116 }
117
118 #[must_use]
119 pub fn node(&self, key: StableNodeKey) -> Option<&Node> {
120 self.node_slot(key)?.value.as_ref()
121 }
122
123 #[must_use]
124 pub fn edge(&self, key: StableEdgeKey) -> Option<&Edge> {
125 Some(&self.edge_slot(key)?.value.as_ref()?.value)
126 }
127
128 #[must_use]
129 pub fn node_key(&self, id: &str) -> Option<StableNodeKey> {
130 self.node_by_id.get(id).copied()
131 }
132
133 #[must_use]
134 pub fn edge_endpoints(&self, key: StableEdgeKey) -> Option<EdgeEndpoints<StableNodeKey>> {
135 let edge = self.edge_slot(key)?.value.as_ref()?;
136 Some(EdgeEndpoints::new(edge.source, edge.target))
137 }
138
139 pub fn nodes(&self) -> impl Iterator<Item = (StableNodeKey, &Node)> {
140 self.nodes.iter().enumerate().filter_map(|(slot, entry)| {
141 let node = entry.value.as_ref()?;
142 Some((
143 StableNodeKey::new(u32::try_from(slot).ok()?, entry.generation),
144 node,
145 ))
146 })
147 }
148
149 pub fn edges(&self) -> impl Iterator<Item = (StableEdgeKey, &Edge)> {
150 self.edges.iter().enumerate().filter_map(|(slot, entry)| {
151 let edge = entry.value.as_ref()?;
152 Some((
153 StableEdgeKey::new(u32::try_from(slot).ok()?, entry.generation),
154 &edge.value,
155 ))
156 })
157 }
158
159 #[must_use]
160 pub fn outgoing_edges(
161 &self,
162 node: StableNodeKey,
163 ) -> impl DoubleEndedIterator<Item = StableEdgeKey> + ExactSizeIterator + '_ {
164 self.node_slot(node)
165 .map_or(&[][..], |slot| slot.outgoing.as_slice())
166 .iter()
167 .copied()
168 }
169
170 #[must_use]
171 pub fn incoming_edges(
172 &self,
173 node: StableNodeKey,
174 ) -> impl DoubleEndedIterator<Item = StableEdgeKey> + ExactSizeIterator + '_ {
175 self.node_slot(node)
176 .map_or(&[][..], |slot| slot.incoming.as_slice())
177 .iter()
178 .copied()
179 }
180
181 #[must_use]
182 pub const fn node_count(&self) -> usize {
183 self.node_count
184 }
185
186 #[must_use]
187 pub const fn edge_count(&self) -> usize {
188 self.edge_count
189 }
190
191 #[must_use]
192 pub const fn is_empty(&self) -> bool {
193 self.node_count == 0 && self.edge_count == 0
194 }
195
196 pub(super) fn node_slot(&self, key: StableNodeKey) -> Option<&NodeSlot> {
197 let slot = self.nodes.get(key.index())?;
198 (slot.generation == key.generation() && slot.value.is_some()).then_some(slot)
199 }
200
201 pub(super) fn node_slot_mut(&mut self, key: StableNodeKey) -> Option<&mut NodeSlot> {
202 let slot = self.nodes.get_mut(key.index())?;
203 (slot.generation == key.generation() && slot.value.is_some()).then_some(slot)
204 }
205
206 pub(super) fn edge_slot(&self, key: StableEdgeKey) -> Option<&EdgeSlot> {
207 let slot = self.edges.get(key.index())?;
208 (slot.generation == key.generation() && slot.value.is_some()).then_some(slot)
209 }
210
211 pub(super) fn edge_slot_mut(&mut self, key: StableEdgeKey) -> Option<&mut EdgeSlot> {
212 let slot = self.edges.get_mut(key.index())?;
213 (slot.generation == key.generation() && slot.value.is_some()).then_some(slot)
214 }
215
216 fn allocate_node(&mut self, node: Node) -> Result<StableNodeKey> {
217 if let Some(slot) = self.free_nodes.pop() {
218 let entry = &mut self.nodes[slot as usize];
219 entry.value = Some(node);
220 entry.outgoing.clear();
221 entry.incoming.clear();
222 return Ok(StableNodeKey::new(slot, entry.generation));
223 }
224 let slot =
225 u32::try_from(self.nodes.len()).map_err(|_| GraphError::IndexCapacityExceeded {
226 category: "working nodes",
227 count: self.nodes.len(),
228 })?;
229 self.nodes.push(NodeSlot {
230 generation: 0,
231 value: Some(node),
232 outgoing: Vec::new(),
233 incoming: Vec::new(),
234 });
235 Ok(StableNodeKey::new(slot, 0))
236 }
237
238 fn allocate_edge(&mut self, edge: WorkingEdge) -> Result<StableEdgeKey> {
239 if let Some(slot) = self.free_edges.pop() {
240 let entry = &mut self.edges[slot as usize];
241 entry.value = Some(edge);
242 return Ok(StableEdgeKey::new(slot, entry.generation));
243 }
244 let slot =
245 u32::try_from(self.edges.len()).map_err(|_| GraphError::IndexCapacityExceeded {
246 category: "working edges",
247 count: self.edges.len(),
248 })?;
249 self.edges.push(EdgeSlot {
250 generation: 0,
251 value: Some(edge),
252 });
253 Ok(StableEdgeKey::new(slot, 0))
254 }
255}