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 variant_defaults;
14mod world_query;
15
16use crate::{query_data::derive_query_data_impl, query_filter::derive_query_filter_impl};
17use bevy_ecs_macro_logic::{
18 component::{DeriveComponent, StorageAttribute, StorageTy},
19 map_entities::map_entities,
20};
21use bevy_macro_utils::{
22 derive_label, ensure_no_collision,
23 fq_std::{FQDefault, FQIterator, FQOption, FQResult},
24 get_struct_fields, pascal_to_snake_case, BevyManifest,
25};
26use proc_macro::TokenStream;
27use proc_macro2::{Ident, Span};
28use quote::{format_ident, quote, ToTokens};
29use syn::{
30 parse_macro_input, parse_quote, punctuated::Punctuated, token::Comma, ConstParam, Data,
31 DeriveInput, Fields, GenericParam, TypeParam,
32};
33
34enum BundleFieldKind {
35 Component,
36 Ignore,
37}
38
39const BUNDLE_ATTRIBUTE_NAME: &str = "bundle";
40const BUNDLE_ATTRIBUTE_IGNORE_NAME: &str = "ignore";
41const BUNDLE_ATTRIBUTE_NO_FROM_COMPONENTS: &str = "ignore_from_components";
42
43#[derive(Debug)]
44struct BundleAttributes {
45 impl_from_components: bool,
46}
47
48impl Default for BundleAttributes {
49 fn default() -> Self {
50 Self {
51 impl_from_components: true,
52 }
53 }
54}
55
56#[proc_macro_derive(Bundle, attributes(bundle))]
58pub fn derive_bundle(input: TokenStream) -> TokenStream {
59 let ast = parse_macro_input!(input as DeriveInput);
60 let ecs_path = bevy_ecs_path();
61
62 let mut attributes = BundleAttributes::default();
63
64 for attr in &ast.attrs {
65 if attr.path().is_ident(BUNDLE_ATTRIBUTE_NAME) {
66 let parsing = attr.parse_nested_meta(|meta| {
67 if meta.path.is_ident(BUNDLE_ATTRIBUTE_NO_FROM_COMPONENTS) {
68 attributes.impl_from_components = false;
69 return Ok(());
70 }
71
72 Err(meta.error(format!("Invalid bundle container attribute. Allowed attributes: `{BUNDLE_ATTRIBUTE_NO_FROM_COMPONENTS}`")))
73 });
74
75 if let Err(e) = parsing {
76 return e.into_compile_error().into();
77 }
78 }
79 }
80
81 let fields = match get_struct_fields(&ast.data, "derive(Bundle)") {
82 Ok(fields) => fields,
83 Err(e) => return e.into_compile_error().into(),
84 };
85
86 let mut field_kinds = Vec::with_capacity(fields.len());
87
88 for field in fields {
89 let mut kind = BundleFieldKind::Component;
90
91 for attr in field
92 .attrs
93 .iter()
94 .filter(|a| a.path().is_ident(BUNDLE_ATTRIBUTE_NAME))
95 {
96 if let Err(error) = attr.parse_nested_meta(|meta| {
97 if meta.path.is_ident(BUNDLE_ATTRIBUTE_IGNORE_NAME) {
98 kind = BundleFieldKind::Ignore;
99 Ok(())
100 } else {
101 Err(meta.error(format!(
102 "Invalid bundle attribute. Use `{BUNDLE_ATTRIBUTE_IGNORE_NAME}`"
103 )))
104 }
105 }) {
106 return error.into_compile_error().into();
107 }
108 }
109
110 field_kinds.push(kind);
111 }
112
113 let field_types = fields.iter().map(|field| &field.ty).collect::<Vec<_>>();
114
115 let mut active_field_types = Vec::new();
116 let mut active_field_members = Vec::new();
117 let mut active_field_locals = Vec::new();
118 let mut inactive_field_members = Vec::new();
119 for ((field_member, field_type), field_kind) in
120 fields.members().zip(field_types).zip(field_kinds)
121 {
122 let field_local = format_ident!("field_{}", field_member);
123
124 match field_kind {
125 BundleFieldKind::Component => {
126 active_field_types.push(field_type);
127 active_field_locals.push(field_local);
128 active_field_members.push(field_member);
129 }
130 BundleFieldKind::Ignore => inactive_field_members.push(field_member),
131 }
132 }
133 let generics = ast.generics;
134 let generics_ty_list = generics.type_params().map(|p| p.ident.clone());
135 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
136 let struct_name = &ast.ident;
137
138 let bundle_impl = quote! {
139 unsafe impl #impl_generics #ecs_path::bundle::Bundle for #struct_name #ty_generics #where_clause {
144 fn component_ids(
145 components: &mut #ecs_path::component::ComponentsRegistrator,
146 ) -> impl #FQIterator<Item = #ecs_path::component::ComponentId> + use<#(#generics_ty_list,)*> {
147 ::core::iter::empty()#(.chain(<#active_field_types as #ecs_path::bundle::Bundle>::component_ids(components)))*
148 }
149
150 fn get_component_ids(
151 components: &#ecs_path::component::Components,
152 ) -> impl #FQIterator<Item = #FQOption<#ecs_path::component::ComponentId>> {
153 ::core::iter::empty()#(.chain(<#active_field_types as #ecs_path::bundle::Bundle>::get_component_ids(components)))*
154 }
155 }
156 };
157
158 let dynamic_bundle_impl = quote! {
159 impl #impl_generics #ecs_path::bundle::DynamicBundle for #struct_name #ty_generics #where_clause {
160 type Effect = ();
161 #[allow(unused_variables)]
162 #[inline]
163 unsafe fn get_components(
164 ptr: #ecs_path::ptr::MovingPtr<'_, Self>,
165 func: &mut impl ::core::ops::FnMut(#ecs_path::component::StorageType, #ecs_path::ptr::OwningPtr<'_>)
166 ) {
167 use #ecs_path::__macro_exports::DebugCheckedUnwrap;
168
169 #ecs_path::ptr::deconstruct_moving_ptr!({
170 let #struct_name { #(#active_field_members: #active_field_locals,)* #(#inactive_field_members: _,)* } = ptr;
171 });
172 #(
173 <#active_field_types as #ecs_path::bundle::DynamicBundle>::get_components(
174 #active_field_locals,
175 func
176 );
177 )*
178 }
179
180 #[allow(unused_variables)]
181 #[inline]
182 unsafe fn apply_effect(
183 ptr: #ecs_path::ptr::MovingPtr<'_, ::core::mem::MaybeUninit<Self>>,
184 func: &mut #ecs_path::world::EntityWorldMut<'_>,
185 ) {
186 }
187 }
188 };
189
190 let fqdefault = FQDefault.into_token_stream();
191 let from_components_impl = attributes.impl_from_components.then(|| quote! {
192 unsafe impl #impl_generics #ecs_path::bundle::BundleFromComponents for #struct_name #ty_generics #where_clause {
195 #[allow(unused_variables, non_snake_case)]
196 unsafe fn from_components<__T, __F>(ctx: &mut __T, func: &mut __F) -> Self
197 where
198 __F: ::core::ops::FnMut(&mut __T) -> #ecs_path::ptr::OwningPtr<'_>
199 {
200 Self {
201 #(#active_field_members: <#active_field_types as #ecs_path::bundle::BundleFromComponents>::from_components(ctx, &mut *func),)*
202 #(#inactive_field_members: #fqdefault::default(),)*
203 }
204 }
205 }
206 });
207 TokenStream::from(quote! {
208 #bundle_impl
209 #from_components_impl
210 #dynamic_bundle_impl
211 })
212}
213
214#[proc_macro_derive(MapEntities, attributes(entities))]
216pub fn derive_map_entities(input: TokenStream) -> TokenStream {
217 let ast = parse_macro_input!(input as DeriveInput);
218 let ecs_path = bevy_ecs_path();
219
220 let map_entities_impl = map_entities(
221 &ast.data,
222 &ecs_path,
223 Ident::new("self", Span::call_site()),
224 false,
225 false,
226 None,
227 );
228
229 let struct_name = &ast.ident;
230 let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
231 TokenStream::from(quote! {
232 impl #impl_generics #ecs_path::entity::MapEntities for #struct_name #type_generics #where_clause {
233 fn map_entities<M: #ecs_path::entity::EntityMapper>(&mut self, mapper: &mut M) {
234 #map_entities_impl
235 }
236 }
237 })
238}
239
240#[proc_macro_derive(SystemParam, attributes(system_param))]
242pub fn derive_system_param(input: TokenStream) -> TokenStream {
243 let token_stream = input.clone();
244 let ast = parse_macro_input!(input as DeriveInput);
245
246 match derive_system_param_impl(token_stream, ast) {
247 Ok(t) => t,
248 Err(e) => e.into_compile_error().into(),
249 }
250}
251fn derive_system_param_impl(
252 token_stream: TokenStream,
253 ast: DeriveInput,
254) -> syn::Result<TokenStream> {
255 let fields = get_struct_fields(&ast.data, "derive(SystemParam)")?;
256 let path = bevy_ecs_path();
257
258 let field_locals = fields
259 .members()
260 .map(|m| format_ident!("field{}", m))
261 .collect::<Vec<_>>();
262 let field_members = fields.members().collect::<Vec<_>>();
263 let field_types = fields.iter().map(|f| &f.ty).collect::<Vec<_>>();
264
265 let field_validation_names = fields.members().map(|m| format!("::{}", quote! { #m }));
266 let mut field_validation_messages = Vec::with_capacity(fields.len());
267 for attr in fields
268 .iter()
269 .map(|f| f.attrs.iter().find(|a| a.path().is_ident("system_param")))
270 {
271 let mut field_validation_message = None;
272 if let Some(attr) = attr {
273 attr.parse_nested_meta(|nested| {
274 if nested.path.is_ident("validation_message") {
275 field_validation_message = Some(nested.value()?.parse()?);
276 Ok(())
277 } else {
278 Err(nested.error("Unsupported attribute"))
279 }
280 })?;
281 }
282 field_validation_messages
283 .push(field_validation_message.unwrap_or_else(|| quote! { err.message }));
284 }
285
286 let generics = ast.generics;
287
288 let w = format_ident!("w");
290 let s = format_ident!("s");
291 for lt in generics.lifetimes() {
292 let ident = <.lifetime.ident;
293 if ident != &w && ident != &s {
294 return Err(syn::Error::new_spanned(
295 lt,
296 r#"invalid lifetime name: expected `'w` or `'s`
297 'w -- refers to data stored in the World.
298 's -- refers to data stored in the SystemParam's state.'"#,
299 ));
300 }
301 }
302
303 let (_impl_generics, ty_generics, where_clause) = generics.split_for_impl();
304
305 let lifetimeless_generics: Vec<_> = generics
306 .params
307 .iter()
308 .filter(|g| !matches!(g, GenericParam::Lifetime(_)))
309 .collect();
310
311 let shadowed_lifetimes: Vec<_> = generics.lifetimes().map(|_| quote!('_)).collect();
312
313 let mut punctuated_generics = Punctuated::<_, Comma>::new();
314 punctuated_generics.extend(lifetimeless_generics.iter().map(|g| match g {
315 GenericParam::Type(g) => GenericParam::Type(TypeParam {
316 default: None,
317 ..g.clone()
318 }),
319 GenericParam::Const(g) => GenericParam::Const(ConstParam {
320 default: None,
321 ..g.clone()
322 }),
323 _ => unreachable!(),
324 }));
325
326 let mut punctuated_generic_idents = Punctuated::<_, Comma>::new();
327 punctuated_generic_idents.extend(lifetimeless_generics.iter().map(|g| match g {
328 GenericParam::Type(g) => &g.ident,
329 GenericParam::Const(g) => &g.ident,
330 _ => unreachable!(),
331 }));
332
333 let punctuated_generics_no_bounds: Punctuated<_, Comma> = lifetimeless_generics
334 .iter()
335 .map(|&g| match g.clone() {
336 GenericParam::Type(mut g) => {
337 g.bounds.clear();
338 GenericParam::Type(g)
339 }
340 g => g,
341 })
342 .collect();
343
344 let mut tuple_types: Vec<_> = field_types.iter().map(ToTokens::to_token_stream).collect();
345 let mut tuple_patterns: Vec<_> = field_locals.iter().map(ToTokens::to_token_stream).collect();
346
347 const LIMIT: usize = 16;
350 while tuple_types.len() > LIMIT {
351 let end = Vec::from_iter(tuple_types.drain(..LIMIT));
352 tuple_types.push(parse_quote!( (#(#end,)*) ));
353
354 let end = Vec::from_iter(tuple_patterns.drain(..LIMIT));
355 tuple_patterns.push(parse_quote!( (#(#end,)*) ));
356 }
357 let mut read_only_generics = generics.clone();
360 let read_only_where_clause = read_only_generics.make_where_clause();
361 for field_type in &field_types {
362 read_only_where_clause
363 .predicates
364 .push(syn::parse_quote!(#field_type: #path::system::ReadOnlySystemParam));
365 }
366
367 let fields_alias =
368 ensure_no_collision(format_ident!("__StructFieldsAlias"), token_stream.clone());
369
370 let struct_name = &ast.ident;
371 let state_struct_visibility = &ast.vis;
372 let state_struct_name = ensure_no_collision(format_ident!("FetchState"), token_stream);
373
374 let mut builder_name = None;
375 for meta in ast
376 .attrs
377 .iter()
378 .filter(|a| a.path().is_ident("system_param"))
379 {
380 meta.parse_nested_meta(|nested| {
381 if nested.path.is_ident("builder") {
382 builder_name = Some(format_ident!("{struct_name}Builder"));
383 Ok(())
384 } else {
385 Err(nested.error("Unsupported attribute"))
386 }
387 })?;
388 }
389
390 let builder = builder_name.map(|builder_name| {
391 let builder_type_parameters: Vec<Ident> = field_members.iter().map(|m| format_ident!("B{}", m)).collect();
392 let builder_doc_comment = format!("A [`SystemParamBuilder`] for a [`{struct_name}`].");
393 let builder_struct = quote! {
394 #[doc = #builder_doc_comment]
395 struct #builder_name<#(#[allow(non_camel_case_types, reason = "generated from snake-case field name")] #builder_type_parameters,)*> {
396 #(#field_members: #builder_type_parameters,)*
397 }
398 };
399 let lifetimes: Vec<_> = generics.lifetimes().collect();
400 let generic_struct = quote!{ #struct_name <#(#lifetimes,)* #punctuated_generic_idents> };
401 let builder_impl = quote!{
402 unsafe impl<
404 #(#lifetimes,)*
405 #(#[allow(non_camel_case_types, reason = "generated from snake-case field name")] #builder_type_parameters: #path::system::SystemParamBuilder<#field_types>,)*
406 #punctuated_generics
407 > #path::system::SystemParamBuilder<#generic_struct> for #builder_name<#(#builder_type_parameters,)*>
408 #where_clause
409 {
410 fn build(self, world: &mut #path::world::World) -> <#generic_struct as #path::system::SystemParam>::State {
411 let #builder_name { #(#field_members: #field_locals,)* } = self;
412 #state_struct_name {
413 state: #path::system::SystemParamBuilder::build((#(#tuple_patterns,)*), world)
414 }
415 }
416 }
417 };
418 (builder_struct, builder_impl)
419 });
420 let (builder_struct, builder_impl) = builder.unzip();
421
422 Ok(TokenStream::from(quote! {
423 const _: () = {
427 type #fields_alias <'w, 's, #punctuated_generics_no_bounds> = (#(#tuple_types,)*);
429
430 #[doc(hidden)]
431 #state_struct_visibility struct #state_struct_name <#(#lifetimeless_generics,)*>
432 #where_clause {
433 state: <#fields_alias::<'static, 'static, #punctuated_generic_idents> as #path::system::SystemParam>::State,
434 }
435
436 unsafe impl<#punctuated_generics> #path::system::SystemParam for
437 #struct_name <#(#shadowed_lifetimes,)* #punctuated_generic_idents> #where_clause
438 {
439 type State = #state_struct_name<#punctuated_generic_idents>;
440 type Item<'w, 's> = #struct_name #ty_generics;
441
442 fn init_state(world: &mut #path::world::World) -> Self::State {
443 #state_struct_name {
444 state: <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::init_state(world),
445 }
446 }
447
448 fn init_access(state: &Self::State, system_meta: &mut #path::system::SystemMeta, component_access_set: &mut #path::query::FilteredAccessSet, world: &mut #path::world::World) {
449 <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::init_access(&state.state, system_meta, component_access_set, world);
450 }
451
452 fn apply(state: &mut Self::State, system_meta: &#path::system::SystemMeta, world: &mut #path::world::World) {
453 <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::apply(&mut state.state, system_meta, world);
454 }
455
456 fn queue(state: &mut Self::State, system_meta: &#path::system::SystemMeta, world: #path::world::DeferredWorld) {
457 <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::queue(&mut state.state, system_meta, world);
458 }
459
460 #[inline]
461 unsafe fn get_param<'w, 's>(
462 state: &'s mut Self::State,
463 system_meta: &#path::system::SystemMeta,
464 world: #path::world::unsafe_world_cell::UnsafeWorldCell<'w>,
465 change_tick: #path::change_detection::Tick,
466 ) -> #FQResult<Self::Item<'w, 's>, #path::system::SystemParamValidationError> {
467 let (#(#tuple_patterns,)*) = &mut state.state;
468 #(
469 let #field_locals = unsafe {
470 <#field_types as #path::system::SystemParam>::get_param(#field_locals, system_meta, world, change_tick)
471 }.map_err(|err| #path::system::SystemParamValidationError::new::<Self>(err.skipped, #field_validation_messages, #field_validation_names))?;
472 )*
473 #FQResult::Ok(#struct_name {
474 #(#field_members: #field_locals,)*
475 })
476 }
477 }
478
479 unsafe impl<'w, 's, #punctuated_generics> #path::system::ReadOnlySystemParam for #struct_name #ty_generics #read_only_where_clause {}
481
482 #builder_impl
483 };
484
485 #builder_struct
486 }))
487}
488
489#[proc_macro_derive(QueryData, attributes(query_data))]
491pub fn derive_query_data(input: TokenStream) -> TokenStream {
492 derive_query_data_impl(input)
493}
494
495#[proc_macro_derive(QueryFilter, attributes(query_filter))]
497pub fn derive_query_filter(input: TokenStream) -> TokenStream {
498 derive_query_filter_impl(input)
499}
500
501#[proc_macro_derive(ScheduleLabel)]
505pub fn derive_schedule_label(input: TokenStream) -> TokenStream {
506 let input = parse_macro_input!(input as DeriveInput);
507 let mut trait_path = bevy_ecs_path();
508 trait_path.segments.push(format_ident!("schedule").into());
509 trait_path
510 .segments
511 .push(format_ident!("ScheduleLabel").into());
512 derive_label(input, "ScheduleLabel", &trait_path)
513}
514
515#[proc_macro_derive(SystemSet)]
519pub fn derive_system_set(input: TokenStream) -> TokenStream {
520 let input = parse_macro_input!(input as DeriveInput);
521 let mut trait_path = bevy_ecs_path();
522 trait_path.segments.push(format_ident!("schedule").into());
523 trait_path.segments.push(format_ident!("SystemSet").into());
524 derive_label(input, "SystemSet", &trait_path)
525}
526
527pub(crate) fn bevy_ecs_path() -> syn::Path {
528 BevyManifest::shared(|manifest| manifest.get_path("bevy_ecs"))
529}
530
531pub(crate) fn bevy_settings_path() -> syn::Path {
532 BevyManifest::shared(|manifest| manifest.get_path("bevy-settings"))
533}
534
535#[proc_macro_derive(Event, attributes(event))]
537pub fn derive_event(input: TokenStream) -> TokenStream {
538 event::derive_event(input)
539}
540
541#[proc_macro_derive(EntityEvent, attributes(entity_event, event_target))]
555pub fn derive_entity_event(input: TokenStream) -> TokenStream {
556 event::derive_entity_event(input)
557}
558
559#[proc_macro_derive(Message)]
561pub fn derive_message(input: TokenStream) -> TokenStream {
562 message::derive_message(input)
563}
564
565#[proc_macro_derive(Resource, attributes(component, require))]
586pub fn derive_resource(input: TokenStream) -> TokenStream {
587 let mut ast = parse_macro_input!(input as DeriveInput);
588 TokenStream::from(resource::derive_resource(&mut ast))
589}
590
591#[proc_macro_derive(SettingsGroup, attributes(settings_group))]
633pub fn derive_settings_group(input: TokenStream) -> TokenStream {
634 let input = parse_macro_input!(input as DeriveInput);
635
636 let name = &input.ident;
637
638 let path = bevy_settings_path();
639
640 let (override_group_name, override_key_name, override_file) = {
641 let mut override_group_name: Option<String> = None;
642 let mut override_key_name: Option<String> = None;
643 let mut override_file: Option<String> = None;
644
645 input
646 .attrs
647 .iter()
648 .find(|attr| attr.path().is_ident("settings_group"))
649 .and_then(|attr| {
650 attr.parse_nested_meta(|meta| {
651 if meta.path.is_ident("group") {
652 let value = meta.value()?;
653 let s: syn::LitStr = value.parse()?;
654 override_group_name = Some(s.value());
655 Ok(())
656 } else if meta.path.is_ident("key") {
657 let value = meta.value()?;
658 let s: syn::LitStr = value.parse()?;
659 override_key_name = Some(s.value());
660 Ok(())
661 } else if meta.path.is_ident("file") {
662 let value = meta.value()?;
663 let s: syn::LitStr = value.parse()?;
664 override_file = Some(s.value());
665 Ok(())
666 } else {
667 Err(meta.error("unsupported attribute"))
668 }
669 })
670 .ok()
671 });
672
673 (override_group_name, override_key_name, override_file)
674 };
675
676 let key_name = match &input.data {
677 Data::Struct(data) => match data.fields {
678 Fields::Named(_) if override_key_name.is_some() => {
679 return syn::Error::new(
680 Span::call_site(),
681 "The `key` attribute is not supported for structs with named fields",
682 )
683 .into_compile_error()
684 .into();
685 }
686 Fields::Named(_) => None,
687 Fields::Unnamed(_) | Fields::Unit => {
688 override_key_name.or_else(|| Some(pascal_to_snake_case(&name.to_string())))
689 }
690 },
691 Data::Enum(_) => override_key_name.or(Some(pascal_to_snake_case(&name.to_string()))),
692 Data::Union(_) => {
693 return syn::Error::new(
694 Span::call_site(),
695 "SettingsGroup cannot be derived for unions",
696 )
697 .into_compile_error()
698 .into();
699 }
700 };
701
702 let group_name = override_group_name.unwrap_or(pascal_to_snake_case(&name.to_string()));
703 let key_name = key_name
704 .map(|f| quote! { #FQOption::Some(#f) })
705 .unwrap_or(quote! { #FQOption::None });
706 let file_name = override_file
707 .map(|f| quote! { #FQOption::Some(#f) })
708 .unwrap_or(quote! { #FQOption::None });
709
710 let expanded = quote! {
711 impl #path::SettingsGroup for #name {
712 fn settings_group_name() -> &'static str {
713 #group_name
714 }
715
716 fn settings_key_name() -> #FQOption<&'static str> {
717 #key_name
718 }
719
720 fn settings_source() -> #FQOption<&'static str> {
721 #file_name
722 }
723 }
724 };
725
726 TokenStream::from(expanded)
727}
728
729#[proc_macro_derive(
825 Component,
826 attributes(component, require, relationship, relationship_target, entities)
827)]
828pub fn derive_component(input: TokenStream) -> TokenStream {
829 let mut ast = parse_macro_input!(input as DeriveInput);
830 let derive_component = match DeriveComponent::parse(&ast, StorageAttribute::Allowed) {
831 Ok(value) => value,
832 Err(e) => return e.into_compile_error().into(),
833 };
834 let bevy_ecs = bevy_ecs_path();
835 let impl_component =
836 match derive_component.impl_component(&mut ast, &bevy_ecs, StorageTy::Table) {
837 Ok(value) => value,
838 Err(err) => return err.into_compile_error().into(),
839 };
840 TokenStream::from(impl_component)
841}
842
843#[proc_macro_derive(FromWorld, attributes(from_world))]
845pub fn derive_from_world(input: TokenStream) -> TokenStream {
846 let bevy_ecs_path = bevy_ecs_path();
847 let ast = parse_macro_input!(input as DeriveInput);
848 let name = ast.ident;
849 let (impl_generics, ty_generics, where_clauses) = ast.generics.split_for_impl();
850
851 let (fields, variant_ident) = match &ast.data {
852 Data::Struct(data) => (&data.fields, None),
853 Data::Enum(data) => {
854 match data.variants.iter().find(|variant| {
855 variant
856 .attrs
857 .iter()
858 .any(|attr| attr.path().is_ident("from_world"))
859 }) {
860 Some(variant) => (&variant.fields, Some(&variant.ident)),
861 None => {
862 return syn::Error::new(
863 Span::call_site(),
864 "No variant found with the `#[from_world]` attribute",
865 )
866 .into_compile_error()
867 .into();
868 }
869 }
870 }
871 Data::Union(_) => {
872 return syn::Error::new(
873 Span::call_site(),
874 "#[derive(FromWorld)]` does not support unions",
875 )
876 .into_compile_error()
877 .into();
878 }
879 };
880
881 let field_init_expr = quote!(#bevy_ecs_path::world::FromWorld::from_world(world));
882 let members = fields.members();
883
884 let field_initializers = match variant_ident {
885 Some(variant_ident) => quote!( Self::#variant_ident {
886 #(#members: #field_init_expr),*
887 }),
888 None => quote!( Self {
889 #(#members: #field_init_expr),*
890 }),
891 };
892
893 TokenStream::from(quote! {
894 impl #impl_generics #bevy_ecs_path::world::FromWorld for #name #ty_generics #where_clauses {
895 fn from_world(world: &mut #bevy_ecs_path::world::World) -> Self {
896 #field_initializers
897 }
898 }
899 })
900}
901
902#[proc_macro_derive(FromTemplate, attributes(template, default))]
904pub fn derive_from_template(input: TokenStream) -> TokenStream {
905 template::derive_from_template(input)
906}
907
908#[proc_macro_derive(VariantDefaults)]
910pub fn derive_variant_defaults(input: TokenStream) -> TokenStream {
911 variant_defaults::derive_variant_defaults(input)
912}