1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
extern crate proc_macro;
use quote::quote;
const MAX_TYPES: usize = 10;
#[allow(clippy::or_fun_call)]
#[proc_macro_attribute]
pub fn system(
attr: proc_macro::TokenStream,
item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let name = syn::parse_macro_input!(attr as syn::Ident);
let mut run = syn::parse_macro_input!(item as syn::ItemFn);
assert!(run.sig.ident == "run", "Systems have only one method: run.");
assert!(
run.sig.generics.params.is_empty() && run.sig.generics.where_clause.is_none(),
"run should not take generic arguments nor where clause."
);
match run.sig.output {
syn::ReturnType::Type(_, type_info) => {
if let syn::Type::Tuple(tuple) = &*type_info {
if !tuple.elems.is_empty() {
panic!("run should not return anything.")
}
} else {
panic!("run should not return anything.")
}
}
syn::ReturnType::Default => {}
}
let body = &*run.block;
let mut data = Vec::with_capacity(run.sig.inputs.len());
let mut binding = Vec::with_capacity(run.sig.inputs.len());
run.sig.inputs.iter_mut().for_each(|arg| {
if let syn::FnArg::Typed(syn::PatType { pat, ty, .. }) = arg {
match **ty {
syn::Type::Reference(ref mut reference) => {
if let syn::Type::Path(path) = &*reference.elem {
if path.path.segments.last().unwrap().ident == "Entities" {
if reference.mutability.is_none() {
**ty = quote!(::shipyard::Entities).into();
} else {
**ty = quote!(::shipyard::EntitiesMut).into();
}
} else {
reference.lifetime = reference.lifetime.clone().or(Some(syn::Lifetime::new(
"'a",
proc_macro::Span::call_site().into(),
)));
}
} else {
reference.lifetime = reference.lifetime.clone().or(Some(syn::Lifetime::new(
"'a",
proc_macro::Span::call_site().into(),
)));
}
}
syn::Type::Path(ref mut path) => {
let last = path.path.segments.last_mut().unwrap();
if last.ident == "Not" {
if let syn::PathArguments::AngleBracketed(inner_type) = &mut last.arguments
{
assert!(inner_type.args.len() == 1, "Not will only accept one type and nothing else.");
let arg = inner_type.args.iter_mut().next().unwrap();
if let syn::GenericArgument::Type(inner_type) = arg {
if let syn::Type::Reference(reference) = inner_type {
reference.lifetime = reference.lifetime.clone().or(Some(syn::Lifetime::new(
"'a",
proc_macro::Span::call_site().into(),
)));
} else {
panic!("Not will only work with component storages refered by &T or &mut T.")
}
} else {
unreachable!()
}
}
}
}
_ => {
panic!(
"A system will only accept a type of this list:\n\n\
\t\t\t&T for an immutable reference to T storage\n\
\t\t\t&mut T for a mutable reference to T storage\n\
\t\t\t&Entities for an immutable reference to the entity storage\n\
\t\t\t&mut EntitiesMut for a mutable reference to the entity storage\n\
\t\t\tAllStorages for a mutable reference to the storage of all components\n\
\t\t\tThreadPool for an immutable reference to the rayon::ThreadPool used by the World"
);
}
}
data.push((*ty).clone());
binding.push((**pat).clone());
} else {
unreachable!()
}
});
while data.len() > MAX_TYPES {
for i in 0..(data.len() / MAX_TYPES) {
let ten = &data[(i * MAX_TYPES)..((i + 1) * MAX_TYPES)];
*data[i] = quote!((#(#ten,)*)).into();
data.drain((i + 1)..((i + 1) * MAX_TYPES));
let ten = &binding[i..(i + 10)];
binding[i] = quote!((#(#ten,)*)).into();
binding.drain((i + 1)..((i + 1) * MAX_TYPES));
}
}
(quote! {
struct #name;
impl<'a> ::shipyard::System<'a> for #name {
type Data = (#(#data,)*);
fn run(&self, (#(#binding,)*): <Self::Data as ::shipyard::SystemData<'a>>::View) #body
}
})
.into()
}