|
|
|
@@ -0,0 +1,122 @@ |
|
|
|
use syn::{ |
|
|
|
punctuated::Punctuated, token::Comma, Data, DataStruct, DeriveInput, Field, Fields, |
|
|
|
FieldsNamed, FieldsUnnamed, Ident, Lifetime, Type, WhereClause, WherePredicate, |
|
|
|
}; |
|
|
|
|
|
|
|
pub fn execute(ast: &DeriveInput) -> proc_macro2::TokenStream { |
|
|
|
let name = &ast.ident; |
|
|
|
let mut generics = ast.generics.clone(); |
|
|
|
|
|
|
|
let (fetch_return, tys) = gen_from_body(&ast.data, name); |
|
|
|
let tys = &tys; |
|
|
|
let def_fetch_lt = ast |
|
|
|
.generics |
|
|
|
.lifetimes() |
|
|
|
.next() |
|
|
|
.expect("There has to be at least one lifetime"); |
|
|
|
let impl_fetch_lt = &def_fetch_lt.lifetime; |
|
|
|
|
|
|
|
{ |
|
|
|
let where_clause = generics.make_where_clause(); |
|
|
|
constrain_system_data_types(where_clause, impl_fetch_lt, tys); |
|
|
|
} |
|
|
|
|
|
|
|
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); |
|
|
|
|
|
|
|
quote! { |
|
|
|
impl #impl_generics |
|
|
|
async_ecs::system::SystemData< #impl_fetch_lt > |
|
|
|
for #name #ty_generics #where_clause |
|
|
|
{ |
|
|
|
fn setup(resources: &mut async_ecs::resources::Resources) { |
|
|
|
#( |
|
|
|
<#tys as async_ecs::system::SystemData> :: setup(world); |
|
|
|
)* |
|
|
|
} |
|
|
|
|
|
|
|
fn fetch(world: & #impl_fetch_lt async_ecs::resources::Resources) -> Self { |
|
|
|
#fetch_return |
|
|
|
} |
|
|
|
|
|
|
|
fn reads() -> Vec<async_ecs::resources::ResourceId> { |
|
|
|
let mut r = Vec::new(); |
|
|
|
|
|
|
|
#( { |
|
|
|
let mut reads = <#tys as async_ecs::system::SystemData> :: reads(); |
|
|
|
r.append(&mut reads); |
|
|
|
} )* |
|
|
|
|
|
|
|
r |
|
|
|
} |
|
|
|
|
|
|
|
fn writes() -> Vec<async_ecs::resources::ResourceId> { |
|
|
|
let mut r = Vec::new(); |
|
|
|
|
|
|
|
#( { |
|
|
|
let mut writes = <#tys as async_ecs::system::SystemData> :: writes(); |
|
|
|
r.append(&mut writes); |
|
|
|
} )* |
|
|
|
|
|
|
|
r |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn collect_field_types(fields: &Punctuated<Field, Comma>) -> Vec<Type> { |
|
|
|
fields.iter().map(|x| x.ty.clone()).collect() |
|
|
|
} |
|
|
|
|
|
|
|
fn gen_identifiers(fields: &Punctuated<Field, Comma>) -> Vec<Ident> { |
|
|
|
fields.iter().map(|x| x.ident.clone().unwrap()).collect() |
|
|
|
} |
|
|
|
|
|
|
|
fn constrain_system_data_types(clause: &mut WhereClause, fetch_lt: &Lifetime, tys: &[Type]) { |
|
|
|
for ty in tys.iter() { |
|
|
|
let where_predicate: WherePredicate = parse_quote!(#ty : async_ecs::system::SystemData< #fetch_lt >); |
|
|
|
clause.predicates.push(where_predicate); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn gen_from_body(ast: &Data, name: &Ident) -> (proc_macro2::TokenStream, Vec<Type>) { |
|
|
|
enum DataType { |
|
|
|
Struct, |
|
|
|
Tuple, |
|
|
|
} |
|
|
|
|
|
|
|
let (body, fields) = match *ast { |
|
|
|
Data::Struct(DataStruct { |
|
|
|
fields: Fields::Named(FieldsNamed { named: ref x, .. }), |
|
|
|
.. |
|
|
|
}) => (DataType::Struct, x), |
|
|
|
Data::Struct(DataStruct { |
|
|
|
fields: Fields::Unnamed(FieldsUnnamed { unnamed: ref x, .. }), |
|
|
|
.. |
|
|
|
}) => (DataType::Tuple, x), |
|
|
|
_ => panic!("Enums are not supported"), |
|
|
|
}; |
|
|
|
|
|
|
|
let tys = collect_field_types(fields); |
|
|
|
|
|
|
|
let fetch_return = match body { |
|
|
|
DataType::Struct => { |
|
|
|
let identifiers = gen_identifiers(fields); |
|
|
|
|
|
|
|
quote! { |
|
|
|
#name { |
|
|
|
#( #identifiers: async_ecs::system::SystemData::fetch(world) ),* |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
DataType::Tuple => { |
|
|
|
let count = tys.len(); |
|
|
|
let fetch = vec![quote! { async_ecs::system::SystemData::fetch(world) }; count]; |
|
|
|
|
|
|
|
quote! { |
|
|
|
#name ( #( #fetch ),* ) |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
(fetch_return, tys) |
|
|
|
} |