bevy_ecs_macros/
component.rs1use proc_macro::TokenStream;
2use proc_macro2::{Span, TokenStream as TokenStream2};
3use quote::quote;
4use syn::{parse_macro_input, parse_quote, DeriveInput, Ident, LitStr, Path, Result};
5
6pub fn derive_event(input: TokenStream) -> TokenStream {
7 let mut ast = parse_macro_input!(input as DeriveInput);
8 let bevy_ecs_path: Path = crate::bevy_ecs_path();
9
10 ast.generics
11 .make_where_clause()
12 .predicates
13 .push(parse_quote! { Self: Send + Sync + 'static });
14
15 let struct_name = &ast.ident;
16 let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
17
18 TokenStream::from(quote! {
19 impl #impl_generics #bevy_ecs_path::event::Event for #struct_name #type_generics #where_clause {
20 }
21
22 impl #impl_generics #bevy_ecs_path::component::Component for #struct_name #type_generics #where_clause {
23 const STORAGE_TYPE: #bevy_ecs_path::component::StorageType = #bevy_ecs_path::component::StorageType::SparseSet;
24 }
25 })
26}
27
28pub fn derive_resource(input: TokenStream) -> TokenStream {
29 let mut ast = parse_macro_input!(input as DeriveInput);
30 let bevy_ecs_path: Path = crate::bevy_ecs_path();
31
32 ast.generics
33 .make_where_clause()
34 .predicates
35 .push(parse_quote! { Self: Send + Sync + 'static });
36
37 let struct_name = &ast.ident;
38 let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
39
40 TokenStream::from(quote! {
41 impl #impl_generics #bevy_ecs_path::system::Resource for #struct_name #type_generics #where_clause {
42 }
43 })
44}
45
46pub fn derive_component(input: TokenStream) -> TokenStream {
47 let mut ast = parse_macro_input!(input as DeriveInput);
48 let bevy_ecs_path: Path = crate::bevy_ecs_path();
49
50 let attrs = match parse_component_attr(&ast) {
51 Ok(attrs) => attrs,
52 Err(e) => return e.into_compile_error().into(),
53 };
54
55 let storage = storage_path(&bevy_ecs_path, attrs.storage);
56
57 ast.generics
58 .make_where_clause()
59 .predicates
60 .push(parse_quote! { Self: Send + Sync + 'static });
61
62 let struct_name = &ast.ident;
63 let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
64
65 TokenStream::from(quote! {
66 impl #impl_generics #bevy_ecs_path::component::Component for #struct_name #type_generics #where_clause {
67 const STORAGE_TYPE: #bevy_ecs_path::component::StorageType = #storage;
68 }
69 })
70}
71
72pub const COMPONENT: &str = "component";
73pub const STORAGE: &str = "storage";
74
75struct Attrs {
76 storage: StorageTy,
77}
78
79#[derive(Clone, Copy)]
80enum StorageTy {
81 Table,
82 SparseSet,
83}
84
85const TABLE: &str = "Table";
87const SPARSE_SET: &str = "SparseSet";
88
89fn parse_component_attr(ast: &DeriveInput) -> Result<Attrs> {
90 let mut attrs = Attrs {
91 storage: StorageTy::Table,
92 };
93
94 for meta in ast.attrs.iter().filter(|a| a.path().is_ident(COMPONENT)) {
95 meta.parse_nested_meta(|nested| {
96 if nested.path.is_ident(STORAGE) {
97 attrs.storage = match nested.value()?.parse::<LitStr>()?.value() {
98 s if s == TABLE => StorageTy::Table,
99 s if s == SPARSE_SET => StorageTy::SparseSet,
100 s => {
101 return Err(nested.error(format!(
102 "Invalid storage type `{s}`, expected '{TABLE}' or '{SPARSE_SET}'.",
103 )));
104 }
105 };
106 Ok(())
107 } else {
108 Err(nested.error("Unsupported attribute"))
109 }
110 })?;
111 }
112
113 Ok(attrs)
114}
115
116fn storage_path(bevy_ecs_path: &Path, ty: StorageTy) -> TokenStream2 {
117 let storage_type = match ty {
118 StorageTy::Table => Ident::new("Table", Span::call_site()),
119 StorageTy::SparseSet => Ident::new("SparseSet", Span::call_site()),
120 };
121
122 quote! { #bevy_ecs_path::component::StorageType::#storage_type }
123}