1#![cfg_attr(docsrs, feature(doc_cfg))]
4
5extern crate proc_macro;
6
7mod event;
8mod message;
9mod query_data;
10mod query_filter;
11mod resource;
12mod template;
13mod world_query;
14
15use crate::{query_data::derive_query_data_impl, query_filter::derive_query_filter_impl};
16use bevy_ecs_macro_logic::{
17 component::{DeriveComponent, StorageAttribute, StorageTy},
18 map_entities::map_entities,
19};
20use bevy_macro_utils::{
21 derive_label, ensure_no_collision,
22 fq_std::{FQDefault, FQIterator, FQOption, FQResult},
23 get_struct_fields, pascal_to_snake_case, BevyManifest,
24};
25use proc_macro::TokenStream;
26use proc_macro2::{Ident, Span};
27use quote::{format_ident, quote, ToTokens};
28use syn::{
29 parse_macro_input, parse_quote, punctuated::Punctuated, token::Comma, ConstParam, Data,
30 DeriveInput, Fields, GenericParam, TypeParam,
31};
32
33enum BundleFieldKind {
34 Component,
35 Ignore,
36}
37
38const BUNDLE_ATTRIBUTE_NAME: &str = "bundle";
39const BUNDLE_ATTRIBUTE_IGNORE_NAME: &str = "ignore";
40const BUNDLE_ATTRIBUTE_NO_FROM_COMPONENTS: &str = "ignore_from_components";
41
42#[derive(Debug)]
43struct BundleAttributes {
44 impl_from_components: bool,
45}
46
47impl Default for BundleAttributes {
48 fn default() -> Self {
49 Self {
50 impl_from_components: true,
51 }
52 }
53}
54
55#[proc_macro_derive(Bundle, attributes(bundle))]
59pub fn derive_bundle(input: TokenStream) -> TokenStream {
60 let ast = parse_macro_input!(input as DeriveInput);
61 let ecs_path = bevy_ecs_path();
62
63 let mut attributes = BundleAttributes::default();
64
65 for attr in &ast.attrs {
66 if attr.path().is_ident(BUNDLE_ATTRIBUTE_NAME) {
67 let parsing = attr.parse_nested_meta(|meta| {
68 if meta.path.is_ident(BUNDLE_ATTRIBUTE_NO_FROM_COMPONENTS) {
69 attributes.impl_from_components = false;
70 return Ok(());
71 }
72
73 Err(meta.error(format!("Invalid bundle container attribute. Allowed attributes: `{BUNDLE_ATTRIBUTE_NO_FROM_COMPONENTS}`")))
74 });
75
76 if let Err(e) = parsing {
77 return e.into_compile_error().into();
78 }
79 }
80 }
81
82 let fields = match get_struct_fields(&ast.data, "derive(Bundle)") {
83 Ok(fields) => fields,
84 Err(e) => return e.into_compile_error().into(),
85 };
86
87 let mut field_kinds = Vec::with_capacity(fields.len());
88
89 for field in fields {
90 let mut kind = BundleFieldKind::Component;
91
92 for attr in field
93 .attrs
94 .iter()
95 .filter(|a| a.path().is_ident(BUNDLE_ATTRIBUTE_NAME))
96 {
97 if let Err(error) = attr.parse_nested_meta(|meta| {
98 if meta.path.is_ident(BUNDLE_ATTRIBUTE_IGNORE_NAME) {
99 kind = BundleFieldKind::Ignore;
100 Ok(())
101 } else {
102 Err(meta.error(format!(
103 "Invalid bundle attribute. Use `{BUNDLE_ATTRIBUTE_IGNORE_NAME}`"
104 )))
105 }
106 }) {
107 return error.into_compile_error().into();
108 }
109 }
110
111 field_kinds.push(kind);
112 }
113
114 let field_types = fields.iter().map(|field| &field.ty).collect::<Vec<_>>();
115
116 let mut active_field_types = Vec::new();
117 let mut active_field_members = Vec::new();
118 let mut active_field_locals = Vec::new();
119 let mut inactive_field_members = Vec::new();
120 for ((field_member, field_type), field_kind) in
121 fields.members().zip(field_types).zip(field_kinds)
122 {
123 let field_local = format_ident!("field_{}", field_member);
124
125 match field_kind {
126 BundleFieldKind::Component => {
127 active_field_types.push(field_type);
128 active_field_locals.push(field_local);
129 active_field_members.push(field_member);
130 }
131 BundleFieldKind::Ignore => inactive_field_members.push(field_member),
132 }
133 }
134 let generics = ast.generics;
135 let generics_ty_list = generics.type_params().map(|p| p.ident.clone());
136 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
137 let struct_name = &ast.ident;
138
139 let bundle_impl = quote! {
140 unsafe impl #impl_generics #ecs_path::bundle::Bundle for #struct_name #ty_generics #where_clause {
145 fn component_ids(
146 components: &mut #ecs_path::component::ComponentsRegistrator,
147 ) -> impl #FQIterator<Item = #ecs_path::component::ComponentId> + use<#(#generics_ty_list,)*> {
148 ::core::iter::empty()#(.chain(<#active_field_types as #ecs_path::bundle::Bundle>::component_ids(components)))*
149 }
150
151 fn get_component_ids(
152 components: &#ecs_path::component::Components,
153 ) -> impl #FQIterator<Item = #FQOption<#ecs_path::component::ComponentId>> {
154 ::core::iter::empty()#(.chain(<#active_field_types as #ecs_path::bundle::Bundle>::get_component_ids(components)))*
155 }
156 }
157 };
158
159 let dynamic_bundle_impl = quote! {
160 impl #impl_generics #ecs_path::bundle::DynamicBundle for #struct_name #ty_generics #where_clause {
161 type Effect = ();
162 #[allow(unused_variables)]
163 #[allow(non_snake_case, reason = "deconstruct_moving_ptr uses #active_field_locals as a local binding name")]
164 #[inline]
165 unsafe fn get_components(
166 ptr: #ecs_path::ptr::MovingPtr<'_, Self>,
167 func: &mut impl ::core::ops::FnMut(#ecs_path::component::StorageType, #ecs_path::ptr::OwningPtr<'_>)
168 ) {
169 use #ecs_path::__macro_exports::DebugCheckedUnwrap;
170
171 #ecs_path::ptr::deconstruct_moving_ptr!({
172 let #struct_name { #(#active_field_members: #active_field_locals,)* #(#inactive_field_members: _,)* } = ptr;
173 });
174 #(
175 <#active_field_types as #ecs_path::bundle::DynamicBundle>::get_components(
176 #active_field_locals,
177 func
178 );
179 )*
180 }
181
182 #[allow(unused_variables)]
183 #[inline]
184 unsafe fn apply_effect(
185 ptr: #ecs_path::ptr::MovingPtr<'_, ::core::mem::MaybeUninit<Self>>,
186 func: &mut #ecs_path::world::EntityWorldMut<'_>,
187 ) {
188 }
189 }
190 };
191
192 let fqdefault = FQDefault.into_token_stream();
193 let from_components_impl = attributes.impl_from_components.then(|| quote! {
194 unsafe impl #impl_generics #ecs_path::bundle::BundleFromComponents for #struct_name #ty_generics #where_clause {
197 #[allow(unused_variables, non_snake_case)]
198 unsafe fn from_components<__T, __F>(ctx: &mut __T, func: &mut __F) -> Self
199 where
200 __F: ::core::ops::FnMut(&mut __T) -> #ecs_path::ptr::OwningPtr<'_>
201 {
202 Self {
203 #(#active_field_members: <#active_field_types as #ecs_path::bundle::BundleFromComponents>::from_components(ctx, &mut *func),)*
204 #(#inactive_field_members: #fqdefault::default(),)*
205 }
206 }
207 }
208 });
209 TokenStream::from(quote! {
210 #bundle_impl
211 #from_components_impl
212 #dynamic_bundle_impl
213 })
214}
215
216#[proc_macro_derive(MapEntities, attributes(entities))]
220pub fn derive_map_entities(input: TokenStream) -> TokenStream {
221 let ast = parse_macro_input!(input as DeriveInput);
222 let ecs_path = bevy_ecs_path();
223
224 let map_entities_impl = map_entities(
225 &ast.data,
226 &ecs_path,
227 Ident::new("self", Span::call_site()),
228 false,
229 false,
230 None,
231 );
232
233 let struct_name = &ast.ident;
234 let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
235 TokenStream::from(quote! {
236 impl #impl_generics #ecs_path::entity::MapEntities for #struct_name #type_generics #where_clause {
237 fn map_entities<M: #ecs_path::entity::EntityMapper>(&mut self, mapper: &mut M) {
238 #map_entities_impl
239 }
240 }
241 })
242}
243
244#[proc_macro_derive(SystemParam, attributes(system_param))]
248pub fn derive_system_param(input: TokenStream) -> TokenStream {
249 let token_stream = input.clone();
250 let ast = parse_macro_input!(input as DeriveInput);
251
252 match derive_system_param_impl(token_stream, ast) {
253 Ok(t) => t,
254 Err(e) => e.into_compile_error().into(),
255 }
256}
257fn derive_system_param_impl(
258 token_stream: TokenStream,
259 ast: DeriveInput,
260) -> syn::Result<TokenStream> {
261 let fields = get_struct_fields(&ast.data, "derive(SystemParam)")?;
262 let path = bevy_ecs_path();
263
264 let field_locals = fields
265 .members()
266 .map(|m| format_ident!("field{}", m))
267 .collect::<Vec<_>>();
268 let field_members = fields.members().collect::<Vec<_>>();
269 let field_types = fields.iter().map(|f| &f.ty).collect::<Vec<_>>();
270
271 let field_validation_names = fields.members().map(|m| format!("::{}", quote! { #m }));
272 let mut field_validation_messages = Vec::with_capacity(fields.len());
273 for attr in fields
274 .iter()
275 .map(|f| f.attrs.iter().find(|a| a.path().is_ident("system_param")))
276 {
277 let mut field_validation_message = None;
278 if let Some(attr) = attr {
279 attr.parse_nested_meta(|nested| {
280 if nested.path.is_ident("validation_message") {
281 field_validation_message = Some(nested.value()?.parse()?);
282 Ok(())
283 } else {
284 Err(nested.error("Unsupported attribute"))
285 }
286 })?;
287 }
288 field_validation_messages
289 .push(field_validation_message.unwrap_or_else(|| quote! { err.message }));
290 }
291
292 let generics = ast.generics;
293
294 let w = format_ident!("w");
296 let s = format_ident!("s");
297 for lt in generics.lifetimes() {
298 let ident = <.lifetime.ident;
299 if ident != &w && ident != &s {
300 return Err(syn::Error::new_spanned(
301 lt,
302 r#"invalid lifetime name: expected `'w` or `'s`
303 'w -- refers to data stored in the World.
304 's -- refers to data stored in the SystemParam's state.'"#,
305 ));
306 }
307 }
308
309 let (_impl_generics, ty_generics, where_clause) = generics.split_for_impl();
310
311 let lifetimeless_generics: Vec<_> = generics
312 .params
313 .iter()
314 .filter(|g| !matches!(g, GenericParam::Lifetime(_)))
315 .collect();
316
317 let shadowed_lifetimes: Vec<_> = generics.lifetimes().map(|_| quote!('_)).collect();
318
319 let mut punctuated_generics = Punctuated::<_, Comma>::new();
320 punctuated_generics.extend(lifetimeless_generics.iter().map(|g| match g {
321 GenericParam::Type(g) => GenericParam::Type(TypeParam {
322 default: None,
323 ..g.clone()
324 }),
325 GenericParam::Const(g) => GenericParam::Const(ConstParam {
326 default: None,
327 ..g.clone()
328 }),
329 _ => unreachable!(),
330 }));
331
332 let mut punctuated_generic_idents = Punctuated::<_, Comma>::new();
333 punctuated_generic_idents.extend(lifetimeless_generics.iter().map(|g| match g {
334 GenericParam::Type(g) => &g.ident,
335 GenericParam::Const(g) => &g.ident,
336 _ => unreachable!(),
337 }));
338
339 let punctuated_generics_no_bounds: Punctuated<_, Comma> = lifetimeless_generics
340 .iter()
341 .map(|&g| match g.clone() {
342 GenericParam::Type(mut g) => {
343 g.bounds.clear();
344 GenericParam::Type(g)
345 }
346 g => g,
347 })
348 .collect();
349
350 let mut tuple_types: Vec<_> = field_types.iter().map(ToTokens::to_token_stream).collect();
351 let mut tuple_patterns: Vec<_> = field_locals.iter().map(ToTokens::to_token_stream).collect();
352
353 const LIMIT: usize = 16;
356 while tuple_types.len() > LIMIT {
357 let end = Vec::from_iter(tuple_types.drain(..LIMIT));
358 tuple_types.push(parse_quote!( (#(#end,)*) ));
359
360 let end = Vec::from_iter(tuple_patterns.drain(..LIMIT));
361 tuple_patterns.push(parse_quote!( (#(#end,)*) ));
362 }
363 let mut read_only_generics = generics.clone();
366 let read_only_where_clause = read_only_generics.make_where_clause();
367 for field_type in &field_types {
368 read_only_where_clause
369 .predicates
370 .push(syn::parse_quote!(#field_type: #path::system::ReadOnlySystemParam));
371 }
372
373 let fields_alias =
374 ensure_no_collision(format_ident!("__StructFieldsAlias"), token_stream.clone());
375
376 let struct_name = &ast.ident;
377 let state_struct_visibility = &ast.vis;
378 let state_struct_name = ensure_no_collision(format_ident!("FetchState"), token_stream);
379
380 let mut builder_name = None;
381 for meta in ast
382 .attrs
383 .iter()
384 .filter(|a| a.path().is_ident("system_param"))
385 {
386 meta.parse_nested_meta(|nested| {
387 if nested.path.is_ident("builder") {
388 builder_name = Some(format_ident!("{struct_name}Builder"));
389 Ok(())
390 } else {
391 Err(nested.error("Unsupported attribute"))
392 }
393 })?;
394 }
395
396 let builder = builder_name.map(|builder_name| {
397 let builder_type_parameters: Vec<Ident> = field_members.iter().map(|m| format_ident!("B{}", m)).collect();
398 let builder_doc_comment = format!("A [`SystemParamBuilder`] for a [`{struct_name}`].");
399 let builder_struct = quote! {
400 #[doc = #builder_doc_comment]
401 struct #builder_name<#(#[allow(non_camel_case_types, reason = "generated from snake-case field name")] #builder_type_parameters,)*> {
402 #(#field_members: #builder_type_parameters,)*
403 }
404 };
405 let lifetimes: Vec<_> = generics.lifetimes().collect();
406 let generic_struct = quote!{ #struct_name <#(#lifetimes,)* #punctuated_generic_idents> };
407 let builder_impl = quote!{
408 unsafe impl<
410 #(#lifetimes,)*
411 #(#[allow(non_camel_case_types, reason = "generated from snake-case field name")] #builder_type_parameters: #path::system::SystemParamBuilder<#field_types>,)*
412 #punctuated_generics
413 > #path::system::SystemParamBuilder<#generic_struct> for #builder_name<#(#builder_type_parameters,)*>
414 #where_clause
415 {
416 fn build(self, world: &mut #path::world::World) -> <#generic_struct as #path::system::SystemParam>::State {
417 let #builder_name { #(#field_members: #field_locals,)* } = self;
418 #state_struct_name {
419 state: #path::system::SystemParamBuilder::build((#(#tuple_patterns,)*), world)
420 }
421 }
422 }
423 };
424 (builder_struct, builder_impl)
425 });
426 let (builder_struct, builder_impl) = builder.unzip();
427
428 Ok(TokenStream::from(quote! {
429 const _: () = {
433 type #fields_alias <'w, 's, #punctuated_generics_no_bounds> = (#(#tuple_types,)*);
435
436 #[doc(hidden)]
437 #state_struct_visibility struct #state_struct_name <#(#lifetimeless_generics,)*>
438 #where_clause {
439 state: <#fields_alias::<'static, 'static, #punctuated_generic_idents> as #path::system::SystemParam>::State,
440 }
441
442 unsafe impl<#punctuated_generics> #path::system::SystemParam for
443 #struct_name <#(#shadowed_lifetimes,)* #punctuated_generic_idents> #where_clause
444 {
445 type State = #state_struct_name<#punctuated_generic_idents>;
446 type Item<'w, 's> = #struct_name #ty_generics;
447
448 fn init_state(world: &mut #path::world::World) -> Self::State {
449 #state_struct_name {
450 state: <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::init_state(world),
451 }
452 }
453
454 fn init_access(
455 state: &Self::State,
456 system_meta: &mut #path::system::SystemMeta,
457 system_access: &mut #path::system::SystemAccess,
458 world: &mut #path::world::World
459 ) {
460 <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::init_access(&state.state, system_meta, system_access, world);
461 }
462
463 fn apply(state: &mut Self::State, system_meta: &#path::system::SystemMeta, world: &mut #path::world::World) {
464 <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::apply(&mut state.state, system_meta, world);
465 }
466
467 fn queue(state: &mut Self::State, system_meta: &#path::system::SystemMeta, world: #path::world::DeferredWorld) {
468 <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::queue(&mut state.state, system_meta, world);
469 }
470
471 #[inline]
472 unsafe fn get_param<'w, 's>(
473 state: &'s mut Self::State,
474 system_meta: &#path::system::SystemMeta,
475 world: #path::world::unsafe_world_cell::UnsafeWorldCell<'w>,
476 change_tick: #path::change_detection::Tick,
477 ) -> #FQResult<Self::Item<'w, 's>, #path::system::SystemParamValidationError> {
478 let (#(#tuple_patterns,)*) = &mut state.state;
479 #(
480 let #field_locals = unsafe {
481 <#field_types as #path::system::SystemParam>::get_param(#field_locals, system_meta, world, change_tick)
482 }.map_err(|err| #path::system::SystemParamValidationError::new::<Self>(err.skipped, #field_validation_messages, #field_validation_names))?;
483 )*
484 #FQResult::Ok(#struct_name {
485 #(#field_members: #field_locals,)*
486 })
487 }
488 }
489
490 unsafe impl<'w, 's, #punctuated_generics> #path::system::ReadOnlySystemParam for #struct_name #ty_generics #read_only_where_clause {}
492
493 #builder_impl
494 };
495
496 #builder_struct
497 }))
498}
499
500#[proc_macro_derive(QueryData, attributes(query_data))]
504pub fn derive_query_data(input: TokenStream) -> TokenStream {
505 derive_query_data_impl(input)
506}
507
508#[proc_macro_derive(QueryFilter, attributes(query_filter))]
512pub fn derive_query_filter(input: TokenStream) -> TokenStream {
513 derive_query_filter_impl(input)
514}
515
516#[proc_macro_derive(ScheduleLabel)]
522pub fn derive_schedule_label(input: TokenStream) -> TokenStream {
523 let input = parse_macro_input!(input as DeriveInput);
524 let mut trait_path = bevy_ecs_path();
525 trait_path.segments.push(format_ident!("schedule").into());
526 trait_path
527 .segments
528 .push(format_ident!("ScheduleLabel").into());
529 derive_label(input, "ScheduleLabel", &trait_path)
530}
531
532#[proc_macro_derive(SystemSet)]
538pub fn derive_system_set(input: TokenStream) -> TokenStream {
539 let input = parse_macro_input!(input as DeriveInput);
540 let mut trait_path = bevy_ecs_path();
541 trait_path.segments.push(format_ident!("schedule").into());
542 trait_path.segments.push(format_ident!("SystemSet").into());
543 derive_label(input, "SystemSet", &trait_path)
544}
545
546pub(crate) fn bevy_ecs_path() -> syn::Path {
547 BevyManifest::shared(|manifest| manifest.get_path("bevy_ecs"))
548}
549
550pub(crate) fn bevy_settings_path() -> syn::Path {
551 BevyManifest::shared(|manifest| manifest.get_path("bevy-settings"))
552}
553
554#[proc_macro_derive(Event, attributes(event))]
558pub fn derive_event(input: TokenStream) -> TokenStream {
559 event::derive_event(input)
560}
561
562#[proc_macro_derive(EntityEvent, attributes(entity_event, event_target))]
579pub fn derive_entity_event(input: TokenStream) -> TokenStream {
580 event::derive_entity_event(input)
581}
582
583#[proc_macro_derive(Message)]
587pub fn derive_message(input: TokenStream) -> TokenStream {
588 message::derive_message(input)
589}
590
591#[proc_macro_derive(Resource, attributes(component, require))]
614pub fn derive_resource(input: TokenStream) -> TokenStream {
615 let mut ast = parse_macro_input!(input as DeriveInput);
616 TokenStream::from(resource::derive_resource(&mut ast))
617}
618
619#[proc_macro_derive(SettingsGroup, attributes(settings_group))]
671pub fn derive_settings_group(input: TokenStream) -> TokenStream {
672 let input = parse_macro_input!(input as DeriveInput);
673
674 let name = &input.ident;
675
676 let path = bevy_settings_path();
677
678 let (override_group_name, override_key_name, override_file) = {
679 let mut override_group_name: Option<String> = None;
680 let mut override_key_name: Option<String> = None;
681 let mut override_file: Option<String> = None;
682
683 input
684 .attrs
685 .iter()
686 .find(|attr| attr.path().is_ident("settings_group"))
687 .and_then(|attr| {
688 attr.parse_nested_meta(|meta| {
689 if meta.path.is_ident("group") {
690 let value = meta.value()?;
691 let s: syn::LitStr = value.parse()?;
692 override_group_name = Some(s.value());
693 Ok(())
694 } else if meta.path.is_ident("key") {
695 let value = meta.value()?;
696 let s: syn::LitStr = value.parse()?;
697 override_key_name = Some(s.value());
698 Ok(())
699 } else if meta.path.is_ident("file") {
700 let value = meta.value()?;
701 let s: syn::LitStr = value.parse()?;
702 override_file = Some(s.value());
703 Ok(())
704 } else {
705 Err(meta.error("unsupported attribute"))
706 }
707 })
708 .ok()
709 });
710
711 (override_group_name, override_key_name, override_file)
712 };
713
714 let key_name = match &input.data {
715 Data::Struct(data) => match data.fields {
716 Fields::Named(_) if override_key_name.is_some() => {
717 return syn::Error::new(
718 Span::call_site(),
719 "The `key` attribute is not supported for structs with named fields",
720 )
721 .into_compile_error()
722 .into();
723 }
724 Fields::Named(_) => None,
725 Fields::Unnamed(_) | Fields::Unit => {
726 override_key_name.or_else(|| Some(pascal_to_snake_case(&name.to_string())))
727 }
728 },
729 Data::Enum(_) => override_key_name.or(Some(pascal_to_snake_case(&name.to_string()))),
730 Data::Union(_) => {
731 return syn::Error::new(
732 Span::call_site(),
733 "SettingsGroup cannot be derived for unions",
734 )
735 .into_compile_error()
736 .into();
737 }
738 };
739
740 let group_name = override_group_name.unwrap_or(pascal_to_snake_case(&name.to_string()));
741 let key_name = key_name
742 .map(|f| quote! { #FQOption::Some(#f) })
743 .unwrap_or(quote! { #FQOption::None });
744 let file_name = override_file
745 .map(|f| quote! { #FQOption::Some(#f) })
746 .unwrap_or(quote! { #FQOption::None });
747
748 let expanded = quote! {
749 impl #path::SettingsGroup for #name {
750 fn settings_group_name() -> &'static str {
751 #group_name
752 }
753
754 fn settings_key_name() -> #FQOption<&'static str> {
755 #key_name
756 }
757
758 fn settings_source() -> #FQOption<&'static str> {
759 #file_name
760 }
761 }
762 };
763
764 TokenStream::from(expanded)
765}
766
767#[proc_macro_derive(
873 Component,
874 attributes(component, require, relationship, relationship_target, entities)
875)]
876pub fn derive_component(input: TokenStream) -> TokenStream {
877 let mut ast = parse_macro_input!(input as DeriveInput);
878 let derive_component = match DeriveComponent::parse(&ast, StorageAttribute::Allowed) {
879 Ok(value) => value,
880 Err(e) => return e.into_compile_error().into(),
881 };
882 let bevy_ecs = bevy_ecs_path();
883 let impl_component =
884 match derive_component.impl_component(&mut ast, &bevy_ecs, StorageTy::Table) {
885 Ok(value) => value,
886 Err(err) => return err.into_compile_error().into(),
887 };
888 TokenStream::from(impl_component)
889}
890
891#[proc_macro_derive(FromWorld, attributes(from_world))]
895pub fn derive_from_world(input: TokenStream) -> TokenStream {
896 let bevy_ecs_path = bevy_ecs_path();
897 let ast = parse_macro_input!(input as DeriveInput);
898 let name = ast.ident;
899 let (impl_generics, ty_generics, where_clauses) = ast.generics.split_for_impl();
900
901 let (fields, variant_ident) = match &ast.data {
902 Data::Struct(data) => (&data.fields, None),
903 Data::Enum(data) => {
904 match data.variants.iter().find(|variant| {
905 variant
906 .attrs
907 .iter()
908 .any(|attr| attr.path().is_ident("from_world"))
909 }) {
910 Some(variant) => (&variant.fields, Some(&variant.ident)),
911 None => {
912 return syn::Error::new(
913 Span::call_site(),
914 "No variant found with the `#[from_world]` attribute",
915 )
916 .into_compile_error()
917 .into();
918 }
919 }
920 }
921 Data::Union(_) => {
922 return syn::Error::new(
923 Span::call_site(),
924 "#[derive(FromWorld)]` does not support unions",
925 )
926 .into_compile_error()
927 .into();
928 }
929 };
930
931 let field_init_expr = quote!(#bevy_ecs_path::world::FromWorld::from_world(world));
932 let members = fields.members();
933
934 let field_initializers = match variant_ident {
935 Some(variant_ident) => quote!( Self::#variant_ident {
936 #(#members: #field_init_expr),*
937 }),
938 None => quote!( Self {
939 #(#members: #field_init_expr),*
940 }),
941 };
942
943 TokenStream::from(quote! {
944 impl #impl_generics #bevy_ecs_path::world::FromWorld for #name #ty_generics #where_clauses {
945 fn from_world(world: &mut #bevy_ecs_path::world::World) -> Self {
946 #field_initializers
947 }
948 }
949 })
950}
951
952#[proc_macro_derive(FromTemplate, attributes(template, default))]
956pub fn derive_from_template(input: TokenStream) -> TokenStream {
957 template::derive_from_template(input)
958}