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