Async Entity Component System based on the ideas of specs (https://github.com/amethyst/specs)
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

123 wiersze
3.6 KiB

  1. use syn::{
  2. punctuated::Punctuated, token::Comma, Data, DataStruct, DeriveInput, Field, Fields,
  3. FieldsNamed, FieldsUnnamed, Ident, Lifetime, Type, WhereClause, WherePredicate,
  4. };
  5. pub fn execute(ast: &DeriveInput) -> proc_macro2::TokenStream {
  6. let name = &ast.ident;
  7. let mut generics = ast.generics.clone();
  8. let (fetch_return, tys) = gen_from_body(&ast.data, name);
  9. let tys = &tys;
  10. let def_fetch_lt = ast
  11. .generics
  12. .lifetimes()
  13. .next()
  14. .expect("There has to be at least one lifetime");
  15. let impl_fetch_lt = &def_fetch_lt.lifetime;
  16. {
  17. let where_clause = generics.make_where_clause();
  18. constrain_system_data_types(where_clause, impl_fetch_lt, tys);
  19. }
  20. let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
  21. quote! {
  22. impl #impl_generics
  23. async_ecs::system::SystemData< #impl_fetch_lt >
  24. for #name #ty_generics #where_clause
  25. {
  26. fn setup(resources: &mut async_ecs::resources::Resources) {
  27. #(
  28. <#tys as async_ecs::system::SystemData> :: setup(world);
  29. )*
  30. }
  31. fn fetch(world: & #impl_fetch_lt async_ecs::resources::Resources) -> Self {
  32. #fetch_return
  33. }
  34. fn reads() -> Vec<async_ecs::resources::ResourceId> {
  35. let mut r = Vec::new();
  36. #( {
  37. let mut reads = <#tys as async_ecs::system::SystemData> :: reads();
  38. r.append(&mut reads);
  39. } )*
  40. r
  41. }
  42. fn writes() -> Vec<async_ecs::resources::ResourceId> {
  43. let mut r = Vec::new();
  44. #( {
  45. let mut writes = <#tys as async_ecs::system::SystemData> :: writes();
  46. r.append(&mut writes);
  47. } )*
  48. r
  49. }
  50. }
  51. }
  52. }
  53. fn collect_field_types(fields: &Punctuated<Field, Comma>) -> Vec<Type> {
  54. fields.iter().map(|x| x.ty.clone()).collect()
  55. }
  56. fn gen_identifiers(fields: &Punctuated<Field, Comma>) -> Vec<Ident> {
  57. fields.iter().map(|x| x.ident.clone().unwrap()).collect()
  58. }
  59. fn constrain_system_data_types(clause: &mut WhereClause, fetch_lt: &Lifetime, tys: &[Type]) {
  60. for ty in tys.iter() {
  61. let where_predicate: WherePredicate = parse_quote!(#ty : async_ecs::system::SystemData< #fetch_lt >);
  62. clause.predicates.push(where_predicate);
  63. }
  64. }
  65. fn gen_from_body(ast: &Data, name: &Ident) -> (proc_macro2::TokenStream, Vec<Type>) {
  66. enum DataType {
  67. Struct,
  68. Tuple,
  69. }
  70. let (body, fields) = match *ast {
  71. Data::Struct(DataStruct {
  72. fields: Fields::Named(FieldsNamed { named: ref x, .. }),
  73. ..
  74. }) => (DataType::Struct, x),
  75. Data::Struct(DataStruct {
  76. fields: Fields::Unnamed(FieldsUnnamed { unnamed: ref x, .. }),
  77. ..
  78. }) => (DataType::Tuple, x),
  79. _ => panic!("Enums are not supported"),
  80. };
  81. let tys = collect_field_types(fields);
  82. let fetch_return = match body {
  83. DataType::Struct => {
  84. let identifiers = gen_identifiers(fields);
  85. quote! {
  86. #name {
  87. #( #identifiers: async_ecs::system::SystemData::fetch(world) ),*
  88. }
  89. }
  90. }
  91. DataType::Tuple => {
  92. let count = tys.len();
  93. let fetch = vec![quote! { async_ecs::system::SystemData::fetch(world) }; count];
  94. quote! {
  95. #name ( #( #fetch ),* )
  96. }
  97. }
  98. };
  99. (fetch_return, tys)
  100. }