Skip to content

Commit 9103751

Browse files
committed
Cleaned up macro
1 parent 38ed79f commit 9103751

File tree

2 files changed

+160
-66
lines changed

2 files changed

+160
-66
lines changed

crates/bevy_ecs/macros/src/lib.rs

+114-66
Original file line numberDiff line numberDiff line change
@@ -322,14 +322,14 @@ pub fn impl_param_set(_input: TokenStream) -> TokenStream {
322322
}
323323

324324
#[derive(PartialEq, Clone)]
325-
enum SystemParamUsage {
325+
enum SystemParamFieldUsage {
326326
Ignore,
327327
Infallible,
328328
Optional,
329-
Resultful(Ident),
329+
Resultful,
330330
}
331331

332-
impl Parse for SystemParamUsage {
332+
impl Parse for SystemParamFieldUsage {
333333
fn parse(input: ParseStream) -> Result<Self> {
334334
if input.parse::<Option<kw::ignore>>()?.is_some() {
335335
return Ok(Self::Ignore);
@@ -343,20 +343,58 @@ impl Parse for SystemParamUsage {
343343
return Ok(Self::Optional);
344344
}
345345

346+
if input.parse::<Option<kw::resultful>>()?.is_some() {
347+
return Ok(Self::Resultful);
348+
}
349+
350+
return Err(input.error("Expected one of 'ignore', 'infallible', 'optional', or 'resultful'"));
351+
}
352+
}
353+
354+
impl From<SystemParamStructUsage> for SystemParamFieldUsage {
355+
fn from(u: SystemParamStructUsage) -> Self {
356+
match u {
357+
SystemParamStructUsage::Infallible => SystemParamFieldUsage::Infallible,
358+
SystemParamStructUsage::Optional => SystemParamFieldUsage::Optional,
359+
SystemParamStructUsage::Resultful(_) => SystemParamFieldUsage::Resultful
360+
}
361+
}
362+
}
363+
364+
#[derive(PartialEq, Clone)]
365+
enum SystemParamStructUsage {
366+
Infallible,
367+
Optional,
368+
Resultful(Ident),
369+
}
370+
371+
impl Parse for SystemParamStructUsage {
372+
fn parse(input: ParseStream) -> Result<Self> {
373+
if input.parse::<Option<kw::infallible>>()?.is_some() {
374+
return Ok(Self::Infallible);
375+
}
376+
377+
if input.parse::<Option<kw::optional>>()?.is_some() {
378+
return Ok(Self::Optional);
379+
}
380+
346381
if input.parse::<Option<kw::resultful>>()?.is_some() {
347382
let content;
348383
parenthesized!(content in input);
349-
let err_ty = content.parse::<Ident>()?;
384+
let err_ty = match content.parse::<Ident>() {
385+
Ok(o) => o,
386+
Err(_) => return Err(input.error("Expected an identifier for `ResultfulSystemParam::Error`."))
387+
};
350388
return Ok(Self::Resultful(err_ty));
351389
}
352390

353-
return Err(input.error("Expected one of 'ignore', 'infallible', 'optional', or 'resultful'"));
391+
return Err(input.error("Expected one of 'infallible', 'optional', or 'resultful(ErrorType)'"));
354392
}
355393
}
356394

357395
#[derive(Default)]
358396
struct SystemParamAttributes {
359-
pub usage: Option<SystemParamUsage>,
397+
pub usage: Option<SystemParamFieldUsage>,
360398
}
361399

362400
static SYSTEM_PARAM_ATTRIBUTE_NAME: &str = "system_param";
@@ -377,13 +415,10 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
377415
let Some(attr_ident) = attr.path.get_ident() else { continue; };
378416
if attr_ident == SYSTEM_PARAM_ATTRIBUTE_NAME {
379417
if fallibility.is_none() {
380-
let usage = attr.parse_args_with(SystemParamUsage::parse)
381-
.expect("Invalid 'system_param' attribute format.");
382-
if usage == SystemParamUsage::Ignore {
383-
return syn::Error::new(attr.span(), "'ignore' is an invalid option at the struct level.")
384-
.into_compile_error()
385-
.into();
386-
}
418+
let usage = match attr.parse_args_with(SystemParamStructUsage::parse) {
419+
Ok(u) => u,
420+
Err(e) => return e.into_compile_error().into(),
421+
};
387422
fallibility = Some(usage);
388423
} else {
389424
return syn::Error::new(attr.span(), "Multiple `system_param` struct attributes found.")
@@ -393,18 +428,17 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
393428
}
394429
}
395430

396-
let fallibility = fallibility.clone().unwrap_or(SystemParamUsage::Infallible);
431+
let fallibility = fallibility.unwrap_or(SystemParamStructUsage::Infallible);
397432

398433
let associated_error = match fallibility {
399-
SystemParamUsage::Resultful(ref err_ty) => quote! { type Error = #err_ty; },
434+
SystemParamStructUsage::Resultful(ref err_ty) => quote! { type Error = #err_ty; },
400435
_ => quote! { },
401436
};
402437

403438
let mut field_idents = Vec::new();
404439
let mut field_getters = Vec::new();
405440
let mut field_patterns = Vec::new();
406441
let mut field_types = Vec::new();
407-
let mut field_wrapped_types = Vec::new();
408442
let mut ignored_fields = Vec::new();
409443
let mut ignored_field_types = Vec::new();
410444
for (i, field) in fields.iter().enumerate() {
@@ -413,8 +447,10 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
413447
let Some(attr_ident) = attr.path.get_ident() else { continue; };
414448
if attr_ident == SYSTEM_PARAM_ATTRIBUTE_NAME {
415449
if field_attrs.usage.is_none() {
416-
field_attrs.usage = Some(attr.parse_args_with(SystemParamUsage::parse)
417-
.expect("Invalid 'system_param' attribute format."));
450+
field_attrs.usage = Some(match attr.parse_args_with(SystemParamFieldUsage::parse) {
451+
Ok(o) => o,
452+
Err(e) => return e.into_compile_error().into()
453+
});
418454
} else {
419455
return syn::Error::new(attr.span(), "Multiple `system_param` field attributes found.")
420456
.into_compile_error()
@@ -423,9 +459,14 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
423459
}
424460
}
425461

426-
match field_attrs.usage.unwrap_or(fallibility.clone()) {
427-
SystemParamUsage::Ignore => {
428-
ignored_fields.push(field.ident.as_ref().unwrap());
462+
match field_attrs.usage.unwrap_or(fallibility.clone().into()) {
463+
SystemParamFieldUsage::Ignore => {
464+
ignored_fields.push(match field.ident.as_ref() {
465+
Some(s) => s,
466+
None => return syn::Error::new(field.span(), "Field lacks an identifier.")
467+
.into_compile_error()
468+
.into()
469+
});
429470
ignored_field_types.push(&field.ty);
430471
},
431472
field_fallibility @ _ => {
@@ -439,30 +480,32 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
439480
.map(|f| quote! { #f })
440481
.unwrap_or_else(|| quote! { #i }),
441482
);
442-
field_types.push(quote! { #ty });
443-
let (getter, ty) = match fallibility {
444-
SystemParamUsage::Infallible => match field_fallibility {
445-
SystemParamUsage::Infallible => (quote! { #ident }, quote! { #ty }),
446-
SystemParamUsage::Optional => (quote! { #ident.expect("Optional system param was not infallible!") }, quote! { Option<#ty> }),
447-
SystemParamUsage::Resultful(_) => (quote! { #ident.expect("Resultful system param was not infallible!") }, quote! { Result<#ty, <#ty as #bevy_ecs::system::ResultfulSystemParam>::Error> }),
448-
SystemParamUsage::Ignore => unreachable!(),
483+
field_getters.push(match fallibility {
484+
SystemParamStructUsage::Infallible => match field_fallibility {
485+
SystemParamFieldUsage::Infallible => quote! { #ident },
486+
SystemParamFieldUsage::Optional => quote! { #ident.expect("Optional system param was not infallible!") },
487+
SystemParamFieldUsage::Resultful => quote! { #ident.expect("Resultful system param was not infallible!") },
488+
_ => unreachable!(),
449489
},
450-
SystemParamUsage::Optional => match field_fallibility {
451-
SystemParamUsage::Infallible => (quote! { #ident }, quote! { #ty }),
452-
SystemParamUsage::Optional => (quote! { match #ident { Some(s) => s, None => return None, } }, quote! { Option<#ty> }),
453-
SystemParamUsage::Resultful(_) => (quote! { match #ident { Ok(o) => o, Err(_) => return None, } }, quote! { Result<#ty, <#ty as #bevy_ecs::system::ResultfulSystemParam>::Error> }),
454-
SystemParamUsage::Ignore => unreachable!(),
490+
SystemParamStructUsage::Optional => match field_fallibility {
491+
SystemParamFieldUsage::Infallible => quote! { #ident },
492+
SystemParamFieldUsage::Optional => quote! { match #ident { Some(s) => s, None => return None, } },
493+
SystemParamFieldUsage::Resultful => quote! { match #ident { Ok(o) => o, Err(_) => return None, } },
494+
_ => unreachable!(),
455495
},
456-
SystemParamUsage::Resultful(_) => match field_fallibility {
457-
SystemParamUsage::Infallible => (quote! { #ident }, quote! { #ty }),
458-
SystemParamUsage::Optional => (quote! { match #ident { Some(s) => s, None => return Err(#bevy_ecs::system::SystemParamError::<#ty>::default().into()), } }, quote! { Option<#ty> }),
459-
SystemParamUsage::Resultful(_) => (quote! { match #ident { Ok(o) => o, Err(e) => return Err(e.into()), } }, quote! { Result<#ty, <#ty as #bevy_ecs::system::ResultfulSystemParam>::Error> }),
460-
SystemParamUsage::Ignore => unreachable!(),
496+
SystemParamStructUsage::Resultful(_) => match field_fallibility {
497+
SystemParamFieldUsage::Infallible => quote! { #ident },
498+
SystemParamFieldUsage::Optional => quote! { match #ident { Some(s) => s, None => return Err(#bevy_ecs::system::SystemParamError::<#ty>::default().into()), } },
499+
SystemParamFieldUsage::Resultful => quote! { match #ident { Ok(o) => o, Err(e) => return Err(e.into()), } },
500+
_ => unreachable!(),
461501
},
462-
SystemParamUsage::Ignore => unreachable!(),
463-
};
464-
field_getters.push(getter);
465-
field_wrapped_types.push(ty);
502+
});
503+
field_types.push(match field_fallibility {
504+
SystemParamFieldUsage::Infallible => quote! { #ty },
505+
SystemParamFieldUsage::Optional => quote! { Option<#ty> },
506+
SystemParamFieldUsage::Resultful => quote! { Result<#ty, <#ty as #bevy_ecs::system::ResultfulSystemParam>::Error> },
507+
_ => unreachable!(),
508+
});
466509
field_patterns.push(ident);
467510
}
468511
}
@@ -521,28 +564,34 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
521564
_ => unreachable!(),
522565
}));
523566

524-
// Create a where clause for the `ReadOnlySystemParam` impl.
525-
// Ensure that each field implements `ReadOnlySystemParam`.
526-
let mut read_only_generics = generics.clone();
527-
let read_only_where_clause = read_only_generics.make_where_clause();
528-
for field_type in &field_types {
529-
read_only_where_clause
530-
.predicates
531-
.push(syn::parse_quote!(#field_type: #bevy_ecs::system::ReadOnlySystemParam));
532-
}
533-
534567
// If the number of fields exceeds the 16-parameter limit,
535568
// fold the fields into tuples of tuples until we are below the limit.
536569
const LIMIT: usize = 16;
537570
while field_types.len() > LIMIT {
538571
let end = Vec::from_iter(field_types.drain(..LIMIT));
539-
field_types.push(parse_quote!( (#(#end,)*) ));
540-
541-
let end = Vec::from_iter(field_wrapped_types.drain(..LIMIT));
542-
field_wrapped_types.push(parse_quote!( (#(#end,)*) ));
572+
field_types.push(match syn::parse(quote! { (#(#end,)*) }.into()) {
573+
Ok(o) => o,
574+
Err(e) => return e.into_compile_error().into(),
575+
});
543576

544577
let end = Vec::from_iter(field_patterns.drain(..LIMIT));
545-
field_patterns.push(parse_quote!( (#(#end,)*) ));
578+
field_patterns.push(match syn::parse(quote! { (#(#end,)*) }.into()) {
579+
Ok(o) => o,
580+
Err(e) => return e.into_compile_error().into(),
581+
});
582+
}
583+
584+
// Create a where clause for the `ReadOnlySystemParam` impl.
585+
// Ensure that each field implements `ReadOnlySystemParam`.
586+
let mut read_only_generics = generics.clone();
587+
let read_only_where_clause = read_only_generics.make_where_clause();
588+
for field_type in &field_types {
589+
read_only_where_clause
590+
.predicates
591+
.push(match syn::parse(quote! { #field_type: #bevy_ecs::system::ReadOnlySystemParam }.into()) {
592+
Ok(o) => o,
593+
Err(e) => return e.into_compile_error().into(),
594+
});
546595
}
547596

548597
let struct_name = &ast.ident;
@@ -556,10 +605,9 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
556605
};
557606

558607
let (system_param, get_param_return, get_param_output) = match fallibility {
559-
SystemParamUsage::Infallible => (quote! { #bevy_ecs::system::SystemParam }, quote! { Self::Item<'w2, 's2> }, quote! { #get_param_output }),
560-
SystemParamUsage::Optional => (quote! { #bevy_ecs::system::OptionalSystemParam }, quote! { Option<Self::Item<'w2, 's2>> }, quote! { Some(#get_param_output) }),
561-
SystemParamUsage::Resultful(_) => (quote! { #bevy_ecs::system::ResultfulSystemParam }, quote! { Result<Self::Item<'w2, 's2>, <Self::Item<'w2, 's2> as #bevy_ecs::system::ResultfulSystemParam>::Error> }, quote! { Ok(#get_param_output) }),
562-
SystemParamUsage::Ignore => unreachable!(),
608+
SystemParamStructUsage::Infallible => (quote! { #bevy_ecs::system::SystemParam }, quote! { Self::Item<'w2, 's2> }, quote! { #get_param_output }),
609+
SystemParamStructUsage::Optional => (quote! { #bevy_ecs::system::OptionalSystemParam }, quote! { Option<Self::Item<'w2, 's2>> }, quote! { Some(#get_param_output) }),
610+
SystemParamStructUsage::Resultful(_) => (quote! { #bevy_ecs::system::ResultfulSystemParam }, quote! { Result<Self::Item<'w2, 's2>, <Self::Item<'w2, 's2> as #bevy_ecs::system::ResultfulSystemParam>::Error> }, quote! { Ok(#get_param_output) }),
563611
};
564612

565613
TokenStream::from(quote! {
@@ -569,7 +617,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
569617
const _: () = {
570618
#[doc(hidden)]
571619
#state_struct_visibility struct FetchState <'w, 's, #(#lifetimeless_generics,)*> {
572-
state: (#(<#field_wrapped_types as #bevy_ecs::system::SystemParam>::State,)*),
620+
state: (#(<#field_types as #bevy_ecs::system::SystemParam>::State,)*),
573621
marker: std::marker::PhantomData<(
574622
<#bevy_ecs::prelude::Query<'w, 's, ()> as #bevy_ecs::system::SystemParam>::State,
575623
#(fn() -> #ignored_field_types,)*
@@ -583,17 +631,17 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
583631

584632
fn init_state(world: &mut #bevy_ecs::world::World, system_meta: &mut #bevy_ecs::system::SystemMeta) -> Self::State {
585633
FetchState {
586-
state: <(#(#field_wrapped_types,)*) as #bevy_ecs::system::SystemParam>::init_state(world, system_meta),
634+
state: <(#(#field_types,)*) as #bevy_ecs::system::SystemParam>::init_state(world, system_meta),
587635
marker: std::marker::PhantomData,
588636
}
589637
}
590638

591639
fn new_archetype(state: &mut Self::State, archetype: &#bevy_ecs::archetype::Archetype, system_meta: &mut #bevy_ecs::system::SystemMeta) {
592-
<(#(#field_wrapped_types,)*) as #bevy_ecs::system::SystemParam>::new_archetype(&mut state.state, archetype, system_meta)
640+
<(#(#field_types,)*) as #bevy_ecs::system::SystemParam>::new_archetype(&mut state.state, archetype, system_meta)
593641
}
594642

595643
fn apply(state: &mut Self::State, system_meta: &#bevy_ecs::system::SystemMeta, world: &mut #bevy_ecs::world::World) {
596-
<(#(#field_wrapped_types,)*) as #bevy_ecs::system::SystemParam>::apply(&mut state.state, system_meta, world);
644+
<(#(#field_types,)*) as #bevy_ecs::system::SystemParam>::apply(&mut state.state, system_meta, world);
597645
}
598646

599647
unsafe fn get_param<'w2, 's2>(
@@ -603,7 +651,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
603651
change_tick: u32,
604652
) -> #get_param_return {
605653
let (#(#field_patterns,)*) = <
606-
(#(#field_wrapped_types,)*) as #bevy_ecs::system::SystemParam
654+
(#(#field_types,)*) as #bevy_ecs::system::SystemParam
607655
>::get_param(&mut state.state, system_meta, world, change_tick);
608656
#get_param_output
609657
}

crates/bevy_ecs/src/system/system_param.rs

+46
Original file line numberDiff line numberDiff line change
@@ -1730,11 +1730,57 @@ mod tests {
17301730
#[derive(SystemParam)]
17311731
pub struct EncapsulatedParam<'w>(Res<'w, PrivateResource>);
17321732

1733+
#[derive(SystemParam)]
1734+
#[system_param(infallible)]
1735+
pub struct ExplicitInfallibleParam<'w> {
1736+
#[system_param(optional)]
1737+
_r0: Res<'w, R<0>>,
1738+
#[system_param(infallible)]
1739+
_r1: Option<Res<'w, R<1>>>,
1740+
}
1741+
17331742
#[derive(SystemParam)]
17341743
#[system_param(optional)]
17351744
pub struct OptionalParam<'w> {
17361745
_r0: Res<'w, R<0>>,
1746+
#[system_param(optional)]
17371747
_r1: Res<'w, R<1>>,
1748+
#[system_param(infallible)]
17381749
_r2: Res<'w, R<2>>,
17391750
}
1751+
1752+
#[derive(SystemParam)]
1753+
#[system_param(resultful(ResultfulParamErr))]
1754+
pub struct ResultfulParam<'w> {
1755+
#[system_param(optional)]
1756+
_r0: Res<'w, R<0>>,
1757+
#[system_param(optional)]
1758+
_r1: Res<'w, R<1>>,
1759+
}
1760+
1761+
#[derive(Debug)]
1762+
pub enum ResultfulParamErr {
1763+
R0,
1764+
R1,
1765+
}
1766+
1767+
impl std::fmt::Display for ResultfulParamErr {
1768+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1769+
write!(f, "ResultfulParamErr is here!")
1770+
}
1771+
}
1772+
1773+
impl std::error::Error for ResultfulParamErr {}
1774+
1775+
impl From<SystemParamError<Res<'_, R<0>>>> for ResultfulParamErr {
1776+
fn from(_: SystemParamError<Res<'_, R<0>>>) -> Self {
1777+
Self::R0
1778+
}
1779+
}
1780+
1781+
impl From<SystemParamError<Res<'_, R<1>>>> for ResultfulParamErr {
1782+
fn from(_: SystemParamError<Res<'_, R<1>>>) -> Self {
1783+
Self::R1
1784+
}
1785+
}
17401786
}

0 commit comments

Comments
 (0)