Skip to content

Commit b86a0d3

Browse files
committed
world groups
1 parent 39091c0 commit b86a0d3

File tree

2 files changed

+61
-15
lines changed

2 files changed

+61
-15
lines changed

crates/stecs-derive/src/world.rs

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use crate::syn;
22

3+
use std::collections::HashMap;
4+
35
use darling::{ast, FromDeriveInput, FromField};
46
use proc_macro2::TokenStream;
57
use quote::{quote, ToTokens};
@@ -14,11 +16,12 @@ pub struct WorldOpts {
1416
}
1517

1618
#[derive(FromField)]
17-
#[darling(attributes(split))]
19+
#[darling(attributes(groups), forward_attrs)]
1820
struct FieldOpts {
1921
ident: Option<syn::Ident>,
2022
// ty: syn::Type,
21-
// groups: Option<Vec<()>>,
23+
attrs: Vec<syn::Attribute>,
24+
// groups: Option<Vec<syn::LitStr>>,
2225
}
2326

2427
struct Struct {
@@ -31,7 +34,7 @@ struct Struct {
3134
struct Field {
3235
name: syn::Ident,
3336
// ty: syn::Type,
34-
// groups: Vec<()>,
37+
groups: Vec<syn::LitStr>,
3538
}
3639

3740
#[derive(thiserror::Error, Debug)]
@@ -40,6 +43,8 @@ enum ParseError {
4043
NotAStruct,
4144
#[error("field has no name")]
4245
NamelessField,
46+
#[error("`groups` attribute accepts only a list of string literals: {0}")]
47+
InvalidGroups(syn::Error),
4348
}
4449

4550
impl TryFrom<WorldOpts> for Struct {
@@ -58,6 +63,7 @@ impl TryFrom<WorldOpts> for Struct {
5863
Ok(Field {
5964
name,
6065
// ty: field.ty
66+
groups: extract_groups(&field.attrs)?,
6167
})
6268
})
6369
.collect::<Result<Vec<Field>, ParseError>>()?;
@@ -70,6 +76,38 @@ impl TryFrom<WorldOpts> for Struct {
7076
}
7177
}
7278

79+
struct Groups(syn::punctuated::Punctuated<syn::LitStr, syn::Token![,]>);
80+
81+
impl syn::parse::Parse for Groups {
82+
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
83+
let ident: syn::Ident = input.parse()?;
84+
if ident != "groups" {
85+
return Err(syn::Error::new_spanned(ident, "unexpected field"));
86+
}
87+
88+
input.parse::<syn::Token![=]>()?;
89+
90+
let groups;
91+
syn::bracketed!(groups in input);
92+
93+
let groups =
94+
syn::punctuated::Punctuated::<syn::LitStr, syn::Token![,]>::parse_terminated(&groups)?;
95+
96+
Ok(Self(groups))
97+
}
98+
}
99+
100+
fn extract_groups(attrs: &[syn::Attribute]) -> Result<Vec<syn::LitStr>, ParseError> {
101+
let mut result = Vec::new();
102+
for attr in attrs {
103+
let groups = attr
104+
.parse_args::<Groups>()
105+
.map_err(ParseError::InvalidGroups)?;
106+
result.extend(groups.0.into_iter());
107+
}
108+
Ok(result)
109+
}
110+
73111
impl WorldOpts {
74112
pub fn derive(self) -> TokenStream {
75113
let query = Struct::try_from(self).unwrap_or_else(|err| panic!("{err}"));
@@ -102,9 +140,24 @@ impl Struct {
102140
let all_fields = self.fields.iter().map(|field| &field.name);
103141
let query_all = generate_query(quote! { query_all }, all_fields);
104142

143+
let query_groups = {
144+
let mut groups: HashMap<String, Vec<&syn::Ident>> = HashMap::new();
145+
for field in &self.fields {
146+
for group in &field.groups {
147+
groups.entry(group.value()).or_default().push(&field.name);
148+
}
149+
}
150+
groups.into_iter().map(|(group, fields)| {
151+
let name =
152+
syn::Ident::new(&format!("query_{}", group), proc_macro2::Span::call_site());
153+
generate_query(name, fields)
154+
})
155+
};
156+
105157
quote! {
106158
#default
107159
#query_all
160+
#(#query_groups)*
108161
}
109162
}
110163
}

examples/world.rs

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,6 @@ pub struct World {
1010
pub particles: StructOf<Dense<Particle>>,
1111
}
1212

13-
// macro_rules! query_all {
14-
// ($world:expr, $args:tt) => {
15-
// query!([$world.players, $world.enemies, $world.particles], $args)
16-
// };
17-
// }
18-
19-
// macro_rules! actors {
20-
// ($world:expr) => {
21-
// [$world.players, $world.enemies]
22-
// };
23-
// }
24-
2513
#[derive(SplitFields)]
2614
pub struct Position {
2715
pub x: f32,
@@ -35,6 +23,7 @@ pub struct Particle {
3523
}
3624

3725
#[derive(SplitFields)]
26+
#[split(debug)]
3827
pub struct Actor {}
3928

4029
pub enum EnemyAi {
@@ -64,4 +53,8 @@ fn main() {
6453
for position in query_all!(world, (&position.x)) {
6554
println!("entity at position: {}", position);
6655
}
56+
57+
for actor in query_actor!(world, (&actor)) {
58+
println!("actor: {:?}", actor);
59+
}
6760
}

0 commit comments

Comments
 (0)