diff --git a/Cargo.toml b/Cargo.toml index 9cc00ef..d63df5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,8 @@ -[package] -name = "async-ecs" -version = "0.1.0" -authors = ["Bergmann89 "] -edition = "2018" +[workspace] +members = [ + "async-ecs", + "async-ecs-derive" +] -[dependencies] -env_logger = "0.8" -log = "0.4" -mopa = "0.2" -rand = "0.7" -thiserror = "1.0" +[patch.crates-io] +async-ecs-derive = { path = "async-ecs-derive" } diff --git a/async-ecs-derive/Cargo.toml b/async-ecs-derive/Cargo.toml new file mode 100644 index 0000000..8cd1c75 --- /dev/null +++ b/async-ecs-derive/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "async-ecs-derive" +version = "0.1.0" +authors = ["Bergmann89 "] +edition = "2018" + +[dependencies] +syn = "1.0" +quote = "1.0" +proc-macro2 = "1.0" + +[lib] +proc-macro = true diff --git a/async-ecs-derive/src/lib.rs b/async-ecs-derive/src/lib.rs new file mode 100644 index 0000000..5fab973 --- /dev/null +++ b/async-ecs-derive/src/lib.rs @@ -0,0 +1,21 @@ +#![recursion_limit = "256"] + +extern crate proc_macro; +extern crate proc_macro2; +#[macro_use] +extern crate quote; +#[macro_use] +extern crate syn; + +mod system_data; + +use proc_macro::TokenStream; + +#[proc_macro_derive(SystemData)] +pub fn system_data(input: TokenStream) -> TokenStream { + let ast = syn::parse(input).unwrap(); + + let gen = system_data::execute(&ast); + + gen.into() +} diff --git a/async-ecs-derive/src/system_data.rs b/async-ecs-derive/src/system_data.rs new file mode 100644 index 0000000..313b31d --- /dev/null +++ b/async-ecs-derive/src/system_data.rs @@ -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 { + let mut r = Vec::new(); + + #( { + let mut reads = <#tys as async_ecs::system::SystemData> :: reads(); + r.append(&mut reads); + } )* + + r + } + + fn writes() -> Vec { + 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) -> Vec { + fields.iter().map(|x| x.ty.clone()).collect() +} + +fn gen_identifiers(fields: &Punctuated) -> Vec { + 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) { + 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) +} diff --git a/async-ecs/Cargo.toml b/async-ecs/Cargo.toml new file mode 100644 index 0000000..f051d9b --- /dev/null +++ b/async-ecs/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "async-ecs" +version = "0.1.0" +authors = ["Bergmann89 "] +edition = "2018" + +[dependencies] +async-ecs-derive = "0.1" +env_logger = "0.8" +log = "0.4" +mopa = "0.2" +rand = "0.7" +thiserror = "1.0" diff --git a/src/lib.rs b/async-ecs/src/lib.rs similarity index 100% rename from src/lib.rs rename to async-ecs/src/lib.rs diff --git a/src/main.rs b/async-ecs/src/main.rs similarity index 100% rename from src/main.rs rename to async-ecs/src/main.rs diff --git a/src/resources/cell.rs b/async-ecs/src/resources/cell.rs similarity index 100% rename from src/resources/cell.rs rename to async-ecs/src/resources/cell.rs diff --git a/src/resources/mod.rs b/async-ecs/src/resources/mod.rs similarity index 100% rename from src/resources/mod.rs rename to async-ecs/src/resources/mod.rs diff --git a/src/system/accessor.rs b/async-ecs/src/system/accessor.rs similarity index 100% rename from src/system/accessor.rs rename to async-ecs/src/system/accessor.rs diff --git a/src/system/mod.rs b/async-ecs/src/system/mod.rs similarity index 100% rename from src/system/mod.rs rename to async-ecs/src/system/mod.rs diff --git a/src/system/system_data.rs b/async-ecs/src/system/system_data.rs similarity index 97% rename from src/system/system_data.rs rename to async-ecs/src/system/system_data.rs index a4f4c05..382561f 100644 --- a/src/system/system_data.rs +++ b/async-ecs/src/system/system_data.rs @@ -5,9 +5,9 @@ use crate::resources::{ResourceId, Resources}; use super::accessor::{Accessor, StaticAccessor}; pub trait SystemData<'a> { - fn setup(world: &mut Resources); + fn setup(resources: &mut Resources); - fn fetch(world: &'a Resources) -> Self; + fn fetch(resources: &'a Resources) -> Self; fn reads() -> Vec;