1use crate::{components::*, user::TaskUser};
2
3use specs::prelude::*;
4
5pub trait TaskFactory {
8 fn create_task(&self, user: &TaskUser) -> Entity;
9}
10
11impl<'a, T: 'static + Clone + TaskComponent<'a> + Send + Sync> TaskFactory for T {
12 fn create_task(&self, user: &TaskUser) -> Entity {
13 user.make_task_lazy(self.clone())
14 }
15}
16
17impl TaskFactory for Entity {
18 fn create_task(&self, _user: &TaskUser) -> Entity {
19 self.clone()
20 }
21}
22
23#[derive(Clone, Debug, Eq, PartialEq)]
31pub enum Cons<T> {
32 Fork(Box<Cons<T>>, Box<Cons<T>>),
33 Seq(Box<Cons<T>>, Box<Cons<T>>),
34 Task(T),
35 Nil, }
37
38impl<T> Cons<T> {
39 fn remove_nil(self) -> Self {
40 match self {
41 Cons::Seq(head, tail) => match (*head, *tail) {
42 (Cons::Nil, Cons::Nil) => Cons::Nil,
43 (Cons::Nil, t) => t.remove_nil(),
44 (h, Cons::Nil) => h.remove_nil(),
45 (h, t) => Cons::Seq(Box::new(h.remove_nil()), Box::new(t.remove_nil())),
46 },
47 Cons::Fork(head, tail) => match (*head, *tail) {
48 (Cons::Nil, Cons::Nil) => Cons::Nil,
49 (Cons::Nil, t) => t.remove_nil(),
50 (h, Cons::Nil) => h.remove_nil(),
51 (h, t) => Cons::Fork(Box::new(h.remove_nil()), Box::new(t.remove_nil())),
52 },
53 Cons::Task(t) => Cons::Task(t),
54 Cons::Nil => Cons::Nil,
55 }
56 }
57}
58
59pub type TaskGraph = Cons<Box<dyn TaskFactory>>;
63
64impl TaskGraph {
65 fn _assemble(self, fork: Option<Entity>, user: &TaskUser) -> (Entity, Entity) {
66 match self {
67 Cons::Seq(head, tail) => {
68 let (head_first_entity, head_last_entity) = head._assemble(None, user);
69 let (tail_first_entity, tail_last_entity) = tail._assemble(None, user);
70 user.join_lazy(tail_first_entity, head_last_entity);
71
72 (head_first_entity, tail_last_entity)
73 }
74 Cons::Fork(head, tail) => {
75 let fork_entity = if let Some(e) = fork {
76 e
77 } else {
78 user.make_fork_lazy()
79 };
80
81 let (_, head_last_entity) = head._assemble(Some(fork_entity), user);
82 let (_, tail_last_entity) = tail._assemble(Some(fork_entity), user);
83
84 if head_last_entity != fork_entity {
88 user.add_prong_lazy(fork_entity, head_last_entity);
89 }
90 if tail_last_entity != fork_entity {
91 user.add_prong_lazy(fork_entity, tail_last_entity);
92 }
93
94 (fork_entity, fork_entity)
95 }
96 Cons::Task(task) => {
97 let task_entity = task.create_task(user);
98
99 (task_entity, task_entity)
100 }
101 Cons::Nil => panic!("Tried to assemble Cons::Nil, which should always be removed."),
102 }
103 }
104
105 pub fn assemble(self, on_completion: OnCompletion, user: &TaskUser) -> Option<Entity> {
108 let s = self.remove_nil();
109 if let Cons::Nil = s {
110 return None;
111 }
112
113 let (_first_entity, last_entity) = s._assemble(None, user);
114 user.finalize_lazy(last_entity, on_completion);
115
116 Some(last_entity)
117 }
118}
119
120#[macro_export]
125macro_rules! empty_graph {
126 () => {
127 Cons::Nil
128 };
129}
130
131#[macro_export]
133macro_rules! task {
134 (@$task:expr) => {
135 Cons::Task(Box::new($task))
136 };
137}
138
139#[macro_export]
143macro_rules! fork {
144 (@$head:expr, $($tail:tt)*) => (
145 Cons::Fork(Box::new(fork!(@$head)), Box::new(fork!($($tail)*)))
146 );
147 ($head:expr, $($tail:tt)*) => (
148 Cons::Fork(Box::new(fork!($head)), Box::new(fork!($($tail)*)))
149 );
150 (@$task:expr) => (
151 Cons::Task(Box::new($task))
152 );
153 ($head:expr) => ( $head );
154}
155
156#[macro_export]
158macro_rules! seq {
159 (@$head:expr, $($tail:tt)*) => (
160 Cons::Seq(Box::new(seq!(@$head)), Box::new(seq!($($tail)*)))
161 );
162 ($head:expr, $($tail:tt)*) => (
163 Cons::Seq(Box::new(seq!($head)), Box::new(seq!($($tail)*)))
164 );
165 (@$task:expr) => (
166 Cons::Task(Box::new($task))
167 );
168 ($head:expr) => ( $head );
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 #[derive(Clone, Debug, Eq, PartialEq)]
176 struct Foo(u32);
177
178 #[test]
179 fn task_graph_macro_fork_two() {
180 let x = fork!(@Foo(1u32), @Foo(2u32));
181 assert_eq!(
182 x,
183 Cons::Fork(
184 Box::new(Cons::Task(Box::new(Foo(1u32)))),
185 Box::new(Cons::Task(Box::new(Foo(2u32)))),
186 )
187 );
188 }
189
190 #[test]
191 fn task_graph_macro_fork_three() {
192 let x = fork!(@Foo(1u32), @Foo(2u32), @Foo(3u32));
193 assert_eq!(
194 x,
195 Cons::Fork(
196 Box::new(Cons::Task(Box::new(Foo(1u32)))),
197 Box::new(Cons::Fork(
198 Box::new(Cons::Task(Box::new(Foo(2u32)))),
199 Box::new(Cons::Task(Box::new(Foo(3u32)))),
200 )),
201 )
202 );
203 }
204
205 #[test]
206 fn task_graph_macro_nested_fork() {
207 let x: Cons<Box<Foo>> = fork!(@Foo(1u32), fork!(@Foo(2u32), @Foo(3u32)));
208 assert_eq!(
209 x,
210 Cons::Fork(
211 Box::new(Cons::Task(Box::new(Foo(1u32)))),
212 Box::new(Cons::Fork(
213 Box::new(Cons::Task(Box::new(Foo(2u32)))),
214 Box::new(Cons::Task(Box::new(Foo(3u32)))),
215 )),
216 )
217 );
218 }
219
220 #[test]
221 fn task_graph_macro_many_nested() {
222 let x = seq!(
223 @Foo(1u32),
224 fork!(
225 seq!(@Foo(2u32), @Foo(3u32), @Foo(4u32)),
226 @Foo(5u32),
227 @Foo(6u32)
228 ),
229 @Foo(7u32)
230 );
231 let y: Cons<Box<Foo>> = Cons::Seq(
232 Box::new(Cons::Task(Box::new(Foo(1u32)))),
233 Box::new(Cons::Seq(
234 Box::new(Cons::Fork(
235 Box::new(Cons::Seq(
236 Box::new(Cons::Task(Box::new(Foo(2u32)))),
237 Box::new(Cons::Seq(
238 Box::new(Cons::Task(Box::new(Foo(3u32)))),
239 Box::new(Cons::Task(Box::new(Foo(4u32)))),
240 )),
241 )),
242 Box::new(Cons::Fork(
243 Box::new(Cons::Task(Box::new(Foo(5u32)))),
244 Box::new(Cons::Task(Box::new(Foo(6u32)))),
245 )),
246 )),
247 Box::new(Cons::Task(Box::new(Foo(7u32)))),
248 )),
249 );
250 assert_eq!(x, y);
251 }
252
253 #[test]
254 fn remove_nil_from_left_fork() {
255 let x = fork!(Cons::Nil, @Foo(1));
256 assert_eq!(x.remove_nil(), task!(@Foo(1)));
257 }
258
259 #[test]
260 fn remove_nil_from_right_fork() {
261 let x = fork!(@Foo(1), Cons::Nil);
262 assert_eq!(x.remove_nil(), task!(@Foo(1)));
263 }
264
265 #[test]
266 fn remove_all_nils_nested_fork() {
267 let x = fork!(Cons::Nil, fork!(Cons::Nil, @Foo(1)));
268 assert_eq!(x.remove_nil(), task!(@Foo(1)));
269 }
270
271 #[test]
272 fn accumulate_sequence_in_loop() {
273 let mut s = empty_graph!();
274 for i in 0..4 {
275 s = seq!(s, @Foo(i));
276 }
277 assert_eq!(
279 s.remove_nil(),
280 seq!(seq!(seq!(@Foo(0), @Foo(1)), @Foo(2)), @Foo(3))
281 );
282 }
283}