1#[derive(Debug, PartialEq)]
21pub struct Node<Id, Item>
22where
23 Id: Copy + Eq,
24{
25 pub id: Id,
27 pub deps: Vec<Id>,
29 pub value: Item,
31}
32
33impl<Id, Item> Node<Id, Item>
34where
35 Id: Copy + Eq,
36{
37 pub fn new(id: Id, deps: Vec<Id>, value: Item) -> Self {
38 Self { id, deps, value }
39 }
40}
41
42#[derive(PartialEq, Debug)]
43pub enum TopsortError<Id> {
44 TargetNotFound(Id),
46 CyclicDependency(Id),
48}
49
50fn find_index<Id, Item>(domain: &[Node<Id, Item>], target: Id) -> Result<usize, TopsortError<Id>>
51where
52 Id: Copy + Eq,
53{
54 match domain.iter().position(|node| node.id == target) {
55 Some(index) => Ok(index),
56 None => Err(TopsortError::TargetNotFound(target)),
57 }
58}
59
60fn visit<Id, Item, F>(
61 domain: &[Node<Id, Item>],
62 target: Id,
63 cb: &mut F,
64 visited: &mut Vec<bool>,
65 current_path: &mut Vec<Id>,
66) -> Result<(), TopsortError<Id>>
67where
68 Id: Copy + Eq,
69 F: FnMut(&Node<Id, Item>),
70{
71 let index = find_index(domain, target)?;
72
73 if visited[index] {
74 return Ok(());
75 }
76
77 if current_path.contains(&target) {
79 return Err(TopsortError::CyclicDependency(target));
80 }
81
82 current_path.push(target);
84
85 for dep in domain[index].deps.iter() {
87 visit(domain, *dep, cb, visited, current_path)?;
88 }
89
90 cb(&domain[index]);
92 visited[index] = true;
93
94 current_path.pop();
96 Ok(())
97}
98
99pub fn sort_cb<Id, Item, F>(
120 domain: &[Node<Id, Item>],
121 target: Id,
122 cb: &mut F,
123) -> Result<(), TopsortError<Id>>
124where
125 Id: Copy + Eq,
126 F: FnMut(&Node<Id, Item>),
127{
128 let size = domain.into_iter().size_hint().0;
129 let mut visited: Vec<bool> = Vec::with_capacity(size);
130 visited.resize(size, false);
131 let mut current_path: Vec<Id> = Vec::new();
132 visit(domain, target, cb, &mut visited, &mut current_path)
133}
134
135pub fn sort<Id, Item>(domain: &[Node<Id, Item>], target: Id) -> Result<Vec<Item>, TopsortError<Id>>
151where
152 Id: Copy + Eq,
153 Item: Clone,
154{
155 let mut out = Vec::new();
156 sort_cb(domain, target, &mut |node: &Node<_, _>| {
157 out.push(node.value.clone());
158 })?;
159
160 Ok(out)
161}
162
163#[cfg(test)]
164mod tests {
165 #[allow(unused_imports)]
166 use super::*;
167
168 #[test]
169 fn sort_cb_works() {
170 let mut out = Vec::new();
171 let result = sort_cb(
172 &[
173 Node::new(1, vec![2, 3], "hello"),
174 Node::new(2, vec![], "world"),
175 Node::new(3, vec![2], "cat"),
176 ],
177 1,
178 &mut |node: &Node<_, _>| {
179 out.push(node.value);
180 },
181 );
182 assert_eq!(result, Ok(()));
183 assert_eq!(out, vec!["world", "cat", "hello"]);
184 }
185
186 #[test]
187 fn sort_works() {
188 let result = sort(
189 &vec![
190 Node::new(1, vec![2, 3], "hello"),
191 Node::new(2, vec![], "world"),
192 Node::new(3, vec![2], "cat"),
193 ],
194 1,
195 );
196 assert_eq!(result, Ok(vec!["world", "cat", "hello"]));
197 }
198
199 #[test]
200 fn target_not_found() {
201 let result = sort(
202 &vec![
203 Node::new(1, vec![2, 3], "hello"),
204 Node::new(2, vec![], "world"),
205 Node::new(3, vec![2, 4], "cat"),
206 ],
207 1,
208 );
209 assert_eq!(result, Err(TopsortError::TargetNotFound(4)));
210 }
211
212 #[test]
213 fn cyclic_dependency() {
214 let result = sort(
215 &vec![
216 Node::new(1, vec![2, 3], "hello"),
217 Node::new(2, vec![1], "world"),
218 Node::new(3, vec![2], "cat"),
219 ],
220 1,
221 );
222 assert_eq!(result, Err(TopsortError::CyclicDependency(1)));
223 }
224
225 #[test]
226 fn empty_domain() {
227 let result = sort(&[] as &[Node<i32, i32>], 1);
228 assert_eq!(result, Err(TopsortError::TargetNotFound(1)));
229 }
230}