1use std::collections::HashSet;
9use std::fmt;
10
11#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
14#[cfg_attr(
15 feature = "serde",
16 derive(serde::Serialize, serde::Deserialize),
17 serde(transparent)
18)]
19pub struct NodeId(String);
20
21impl NodeId {
22 pub fn as_str(&self) -> &str {
24 &self.0
25 }
26}
27
28impl From<&str> for NodeId {
29 fn from(s: &str) -> Self {
30 Self(s.to_string())
31 }
32}
33
34impl From<String> for NodeId {
35 fn from(s: String) -> Self {
36 Self(s)
37 }
38}
39
40impl fmt::Display for NodeId {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 f.write_str(&self.0)
43 }
44}
45
46#[derive(Debug, Clone, PartialEq, Eq)]
48pub struct Edge {
49 pub source: NodeId,
51 pub target: NodeId,
53}
54
55#[derive(Debug, Default, Clone, PartialEq, Eq)]
61pub struct Graph {
62 nodes: Vec<NodeId>,
63 edges: Vec<Edge>,
64}
65
66impl Graph {
67 pub fn new() -> Self {
69 Self::default()
70 }
71
72 pub fn add_node(&mut self, id: impl Into<NodeId>) -> Result<&NodeId, GraphError> {
74 let id = id.into();
75 if self.contains(&id) {
76 return Err(GraphError::DuplicateNode(id));
77 }
78 self.nodes.push(id);
79 Ok(self.nodes.last().expect("just inserted it"))
80 }
81
82 pub fn add_edge(
85 &mut self,
86 source: impl Into<NodeId>,
87 target: impl Into<NodeId>,
88 ) -> Result<&Edge, GraphError> {
89 let (source, target) = (source.into(), target.into());
90 for end in [&source, &target] {
91 if !self.contains(end) {
92 return Err(GraphError::UnknownNode(end.clone()));
93 }
94 }
95 if self
96 .edges
97 .iter()
98 .any(|e| e.source == source && e.target == target)
99 {
100 return Err(GraphError::DuplicateEdge {
101 from: source,
102 to: target,
103 });
104 }
105 if source == target || self.reaches(&target, &source) {
106 return Err(GraphError::WouldCycle {
107 from: source,
108 to: target,
109 });
110 }
111 self.edges.push(Edge { source, target });
112 Ok(self.edges.last().expect("just inserted it"))
113 }
114
115 pub fn free_id(&self, wanted: &str) -> NodeId {
117 let mut candidate = NodeId::from(wanted);
118 let mut n = 1;
119 while self.contains(&candidate) {
120 n += 1;
121 candidate = NodeId::from(format!("{wanted}_{n}"));
122 }
123 candidate
124 }
125
126 pub fn nodes(&self) -> &[NodeId] {
128 &self.nodes
129 }
130
131 pub fn edges(&self) -> &[Edge] {
133 &self.edges
134 }
135
136 pub fn len(&self) -> usize {
138 self.nodes.len()
139 }
140
141 pub fn is_empty(&self) -> bool {
143 self.nodes.is_empty()
144 }
145
146 pub fn contains(&self, id: &NodeId) -> bool {
148 self.nodes.contains(id)
149 }
150
151 pub fn predecessors(&self, id: &NodeId) -> Vec<&NodeId> {
153 self.edges
154 .iter()
155 .filter(|e| &e.target == id)
156 .map(|e| &e.source)
157 .collect()
158 }
159
160 pub fn successors(&self, id: &NodeId) -> Vec<&NodeId> {
162 self.edges
163 .iter()
164 .filter(|e| &e.source == id)
165 .map(|e| &e.target)
166 .collect()
167 }
168
169 pub fn roots(&self) -> Vec<&NodeId> {
171 self.nodes
172 .iter()
173 .filter(|id| !self.edges.iter().any(|e| e.target == **id))
174 .collect()
175 }
176
177 pub fn leaves(&self) -> Vec<&NodeId> {
179 self.nodes
180 .iter()
181 .filter(|id| !self.edges.iter().any(|e| e.source == **id))
182 .collect()
183 }
184
185 pub fn topological_sort(&self) -> Vec<&NodeId> {
188 let mut pending: Vec<usize> = self
189 .nodes
190 .iter()
191 .map(|id| self.predecessors(id).len())
192 .collect();
193 let mut order = Vec::with_capacity(self.nodes.len());
194 let mut placed = HashSet::new();
195
196 while order.len() < self.nodes.len() {
197 let next = pending
198 .iter()
199 .enumerate()
200 .find(|(i, n)| **n == 0 && !placed.contains(i))
201 .map(|(i, _)| i)
202 .expect("a non-empty DAG always has a node with nothing pending");
203
204 placed.insert(next);
205 let id = &self.nodes[next];
206 for succ in self.successors(id) {
207 let i = self
208 .nodes
209 .iter()
210 .position(|n| n == succ)
211 .expect("an edge only points at nodes of the graph");
212 pending[i] -= 1;
213 }
214 order.push(id);
215 }
216 order
217 }
218
219 fn reaches(&self, from: &NodeId, to: &NodeId) -> bool {
221 let mut frontier = vec![from];
222 let mut seen = HashSet::new();
223 while let Some(current) = frontier.pop() {
224 if current == to {
225 return true;
226 }
227 if seen.insert(current) {
228 frontier.extend(self.successors(current));
229 }
230 }
231 false
232 }
233}
234
235#[derive(Debug, Clone, PartialEq, Eq)]
241pub enum GraphError {
242 DuplicateNode(NodeId),
244 UnknownNode(NodeId),
246 DuplicateEdge {
248 from: NodeId,
250 to: NodeId,
252 },
253 WouldCycle {
255 from: NodeId,
257 to: NodeId,
259 },
260}
261
262impl fmt::Display for GraphError {
263 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
264 match self {
265 Self::DuplicateNode(id) => write!(f, "there is already a node called `{id}`"),
266 Self::UnknownNode(id) => write!(f, "`{id}` names no node of this graph"),
267 Self::DuplicateEdge { from, to } => {
268 write!(f, "the edge `{from}` → `{to}` already exists")
269 }
270 Self::WouldCycle { from, to } => write!(
271 f,
272 "the edge `{from}` → `{to}` would close a cycle: `{to}` already reaches `{from}`"
273 ),
274 }
275 }
276}
277
278impl std::error::Error for GraphError {}