Skip to main content

hecs_schedule/
system.rs

1//! Provides system which are an abstraction for anything that can be executed
2//! against a [Context](crate::Context).
3use std::{any::type_name, borrow::Cow};
4
5use crate::{
6    borrow::{Borrows, ComponentBorrow, ContextBorrow, IntoBorrow},
7    Context, Result,
8};
9
10/// System name alias
11pub type SystemName = Cow<'static, str>;
12
13/// Trait which defines any function or type that can operate on a world or
14/// other context.
15pub trait System<Args, Ret> {
16    /// Executes the by borrowing from context
17    fn execute(&mut self, context: &Context) -> Result<()>;
18    /// Returns the system name. Used for debug purposes
19    fn name(&self) -> SystemName;
20
21    /// Returns which data will be accessed
22    fn borrows() -> Borrows;
23
24    /// Wrap the system with a custom name
25    fn named<S: Into<Cow<'static, str>>>(self, name: S) -> NamedSystem<Self>
26    where
27        Self: Sized,
28    {
29        NamedSystem {
30            inner: self,
31            name: name.into(),
32        }
33    }
34}
35
36macro_rules! tuple_impl {
37    ($($name: ident), *) => {
38        impl<Func, $($name,)  *> System<($($name,)*), ()> for Func
39        where
40            for<'a, 'b> &'b mut Func:
41                FnMut($($name,)*) +
42                FnMut($(<$name::Borrow as ContextBorrow<'a>>::Target),*),
43                $($name: IntoBorrow + ComponentBorrow,)*
44        {
45            fn execute(&mut self, context: &Context) -> Result<()> {
46                let mut func = self;
47                (&mut func)($($name::Borrow::borrow(context)?), *);
48                Ok(())
49            }
50
51            fn name(&self) -> SystemName {
52                type_name::<Func>().into()
53                // format!("System<{:?}>", ($(type_name::<$name>(),)* )).into()
54            }
55
56            fn borrows() -> Borrows {
57                ([].iter()
58                    $(.chain($name::borrows().iter())) *).cloned()
59                .collect()
60            }
61        }
62
63        impl<Err, Func, $($name,) *> System<($($name,)*), std::result::Result<(), Err>> for Func
64        where
65            Err: Into<anyhow::Error>,
66            for<'a, 'b> &'b mut Func:
67                FnMut($($name,)*) -> std::result::Result<(), Err> +
68                FnMut($(<$name::Borrow as ContextBorrow<'a>>::Target),*) -> std::result::Result<(), Err>,
69                $($name: IntoBorrow + ComponentBorrow,)*
70        {
71            fn execute(&mut self, context: &Context) -> Result<()> {
72                let mut func = self;
73                match (&mut func)($($name::Borrow::borrow(context)?), *) {
74                    Ok(()) => Ok(()),
75                    Err(e) => Err(crate::Error::SystemError(<Self as System<($($name,)*), std::result::Result<(), Err>>>::name(func), e.into())),
76                }
77            }
78
79            fn name(&self) -> SystemName {
80                type_name::<Func>().into()
81                // format!("System<{:?}> -> Result<(), {:?}>", ($(type_name::<$name>(),)* ), type_name::<Err>()).into()
82            }
83
84            fn borrows() -> Borrows {
85                ([].iter()
86                    $(.chain($name::borrows().iter())) *).cloned()
87                .collect()
88            }
89        }
90    };
91}
92
93impl<F: FnMut()> System<(), ()> for F {
94    fn execute(&mut self, _: &Context) -> Result<()> {
95        (self)();
96        Ok(())
97    }
98
99    fn name(&self) -> SystemName {
100        "System<()>".into()
101    }
102
103    fn borrows() -> Borrows {
104        Borrows::default()
105    }
106}
107
108impl<Err: Into<anyhow::Error>, F: FnMut() -> std::result::Result<(), Err>>
109    System<(), std::result::Result<(), Err>> for F
110{
111    fn execute(&mut self, _: &Context) -> Result<()> {
112        (self)().map_err(|e| crate::Error::SystemError(self.name(), e.into()))
113    }
114
115    fn name(&self) -> SystemName {
116        "System<()>".into()
117    }
118
119    fn borrows() -> Borrows {
120        Borrows::default()
121    }
122}
123
124/// A wrapper for providing a system with a name
125pub struct NamedSystem<F> {
126    inner: F,
127    name: Cow<'static, str>,
128}
129
130impl<F: System<Args, Ret>, Args, Ret> System<Args, Ret> for NamedSystem<F> {
131    fn execute(&mut self, context: &Context) -> Result<()> {
132        self.inner.execute(context)
133    }
134
135    fn name(&self) -> SystemName {
136        self.name.clone()
137    }
138
139    fn borrows() -> Borrows {
140        F::borrows()
141    }
142}
143
144impl_for_tuples!(tuple_impl);
145
146#[cfg(test)]
147mod tests {
148    use crate::{system::System, Context, GenericWorld, IntoData, Read, SubWorld};
149    use hecs::World;
150
151    use anyhow::{ensure, Result};
152
153    fn count_system(val: Read<i32>) {
154        assert_eq!(*val, 6);
155    }
156
157    #[test]
158    fn simple_system() {
159        struct App {
160            name: &'static str,
161        }
162
163        let mut val = 6_i32;
164
165        let mut app = App {
166            name: "hecs-schedule",
167        };
168
169        let mut world = World::default();
170
171        let a = world.spawn(("a", 3));
172        let b = world.spawn(("b", 42));
173        let c = world.spawn(("c", 8));
174
175        let data = unsafe { (&mut world, &mut app, &mut val).into_data(&mut ()) };
176
177        let context = Context::new(&data);
178
179        let mut count_closure = |w: SubWorld<&i32>| assert_eq!(w.query::<&i32>().iter().count(), 3);
180        let mut foo = |_: SubWorld<&i32>| {};
181        let mut name_query_system = |w: SubWorld<&String>| -> Result<()> {
182            let name = w.get::<String>(a)?;
183            eprintln!("Name: {:?}", *name);
184            Ok(())
185        };
186
187        let mut name_check_system = |w: SubWorld<&&'static str>| -> anyhow::Result<()> {
188            for (e, n) in [(a, "a"), (b, "b"), (c, "c")] {
189                let name = w.get::<&str>(e)?;
190                ensure!(*name == n, "Names did not match");
191            }
192
193            Ok(())
194        };
195
196        let mut rename_system = |w: SubWorld<&mut String>, a: Read<App>| -> anyhow::Result<()> {
197            ensure!(a.name == "hecs-schedule", "App name did not match");
198
199            w.try_query::<&mut String>()?
200                .iter()
201                .for_each(|(_, name)| *name = a.name.into());
202
203            Ok(())
204        };
205
206        let mut check_rename_system = |w: SubWorld<&String>| -> anyhow::Result<()> {
207            ensure!(
208                w.try_query::<&String>()?
209                    .iter()
210                    .all(|(_, name)| name == "hecs-schedule"),
211                "Names were not properly updated"
212            );
213
214            Ok(())
215        };
216
217        count_system.execute(&context).unwrap();
218        foo.execute(&context).unwrap();
219        count_closure.execute(&context).unwrap();
220        assert!(name_query_system.execute(&context).is_err());
221        name_check_system.execute(&context).unwrap();
222        rename_system.execute(&context).unwrap();
223        check_rename_system.execute(&context).unwrap();
224    }
225}