1use std::{any::type_name, borrow::Cow};
4
5use crate::{
6 borrow::{Borrows, ComponentBorrow, ContextBorrow, IntoBorrow},
7 Context, Result,
8};
9
10pub type SystemName = Cow<'static, str>;
12
13pub trait System<Args, Ret> {
16 fn execute(&mut self, context: &Context) -> Result<()>;
18 fn name(&self) -> SystemName;
20
21 fn borrows() -> Borrows;
23
24 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 }
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 }
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
124pub 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}