@@ -266,19 +266,26 @@ pub fn derive_map_entities(input: TokenStream) -> TokenStream {
266266 }
267267 } )
268268}
269-
270269/// Implement `SystemParam` to use a struct as a parameter in a system
271270#[ proc_macro_derive( SystemParam , attributes( system_param) ) ]
272271pub fn derive_system_param ( input : TokenStream ) -> TokenStream {
273272 let token_stream = input. clone ( ) ;
274273 let ast = parse_macro_input ! ( input as DeriveInput ) ;
274+ match derive_system_param_impl ( token_stream, ast) {
275+ Ok ( t) => t,
276+ Err ( e) => e. into_compile_error ( ) . into ( ) ,
277+ }
278+ }
279+
280+ fn derive_system_param_impl (
281+ token_stream : TokenStream ,
282+ ast : DeriveInput ,
283+ ) -> syn:: Result < TokenStream > {
275284 let Data :: Struct ( DataStruct { fields, .. } ) = ast. data else {
276- return syn:: Error :: new (
285+ return Err ( syn:: Error :: new (
277286 ast. span ( ) ,
278287 "Invalid `SystemParam` type: expected a `struct`" ,
279- )
280- . into_compile_error ( )
281- . into ( ) ;
288+ ) ) ;
282289 } ;
283290 let path = bevy_ecs_path ( ) ;
284291
@@ -289,6 +296,25 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
289296 let field_members = fields. members ( ) . collect :: < Vec < _ > > ( ) ;
290297 let field_types = fields. iter ( ) . map ( |f| & f. ty ) . collect :: < Vec < _ > > ( ) ;
291298
299+ let field_names = fields. members ( ) . map ( |m| format_ident ! ( "::{}" , m) ) ;
300+
301+ let mut field_messages = Vec :: new ( ) ;
302+ for attr in fields
303+ . iter ( )
304+ . filter_map ( |f| f. attrs . iter ( ) . find ( |a| a. path ( ) . is_ident ( "system_param" ) ) )
305+ {
306+ let mut field_message = None ;
307+ attr. parse_nested_meta ( |nested| {
308+ if nested. path . is_ident ( "validation_message" ) {
309+ field_message = Some ( nested. value ( ) ?. parse ( ) ?) ;
310+ Ok ( ( ) )
311+ } else {
312+ Err ( nested. error ( "Unsupported attribute" ) )
313+ }
314+ } ) ?;
315+ field_messages. push ( field_message. unwrap_or_else ( || quote ! { err. message } ) ) ;
316+ }
317+
292318 let generics = ast. generics ;
293319
294320 // Emit an error if there's any unrecognized lifetime names.
@@ -297,14 +323,12 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
297323 let w = format_ident ! ( "w" ) ;
298324 let s = format_ident ! ( "s" ) ;
299325 if ident != & w && ident != & s {
300- return syn:: Error :: new_spanned (
326+ return Err ( syn:: Error :: new_spanned (
301327 lt,
302328 r#"invalid lifetime name: expected `'w` or `'s`
303329 'w -- refers to data stored in the World.
304330 's -- refers to data stored in the SystemParam's state.'"# ,
305- )
306- . into_compile_error ( )
307- . into ( ) ;
331+ ) ) ;
308332 }
309333 }
310334
@@ -386,16 +410,14 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
386410 . iter ( )
387411 . filter ( |a| a. path ( ) . is_ident ( "system_param" ) )
388412 {
389- if let Err ( e ) = meta. parse_nested_meta ( |nested| {
413+ meta. parse_nested_meta ( |nested| {
390414 if nested. path . is_ident ( "builder" ) {
391415 builder_name = Some ( format_ident ! ( "{struct_name}Builder" ) ) ;
392416 Ok ( ( ) )
393417 } else {
394418 Err ( nested. error ( "Unsupported attribute" ) )
395419 }
396- } ) {
397- return e. into_compile_error ( ) . into ( ) ;
398- }
420+ } ) ?;
399421 }
400422
401423 let builder = builder_name. map ( |builder_name| {
@@ -430,7 +452,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
430452 } ) ;
431453 let ( builder_struct, builder_impl) = builder. unzip ( ) ;
432454
433- TokenStream :: from ( quote ! {
455+ Ok ( TokenStream :: from ( quote ! {
434456 // We define the FetchState struct in an anonymous scope to avoid polluting the user namespace.
435457 // The struct can still be accessed via SystemParam::State, e.g. MessageReaderState can be accessed via
436458 // <MessageReader<'static, 'static, T> as SystemParam>::State
@@ -505,7 +527,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
505527 } ;
506528
507529 #builder_struct
508- } )
530+ } ) )
509531}
510532
511533/// Implement `QueryData` to use a struct as a data parameter in a query
0 commit comments