Path: blob/main/crates/bevy_ecs/macros/src/template.rs
30636 views
use bevy_macro_utils::{fq_std::FQDefault, BevyManifest};1use proc_macro::TokenStream;2use quote::{format_ident, quote};3use syn::{4parse::ParseStream, parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned,5Data, DeriveInput, Fields, FieldsUnnamed, Ident, Index, Path, Result, Token, WhereClause,6};78const TEMPLATE_DEFAULT_ATTRIBUTE: &str = "default";9const TEMPLATE_ATTRIBUTE: &str = "template";10const BUILT_IN_ATTRIBUTE: &str = "built_in";1112pub(crate) fn derive_from_template(input: TokenStream) -> TokenStream {13let ast = parse_macro_input!(input as DeriveInput);14let bevy_ecs = BevyManifest::shared(|manifest| manifest.get_path("bevy_ecs"));1516let type_ident = &ast.ident;17let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();1819let template_ident = format_ident!("{type_ident}Template");2021let is_pub = matches!(ast.vis, syn::Visibility::Public(_));22let maybe_pub = if is_pub { quote!(pub) } else { quote!() };2324let template = match &ast.data {25Data::Struct(data_struct) => {26let result = match struct_impl(&data_struct.fields, &bevy_ecs, false) {27Ok(result) => result,28Err(err) => return err.into_compile_error().into(),29};30let StructImpl {31template_fields,32template_field_builds,33template_field_defaults,34template_field_clones,35..36} = result;37match &data_struct.fields {38Fields::Named(_) => {39quote! {40#[allow(missing_docs)]41#maybe_pub struct #template_ident #impl_generics #where_clause {42#(#template_fields,)*43}4445impl #impl_generics #bevy_ecs::template::Template for #template_ident #type_generics #where_clause {46type Output = #type_ident #type_generics;47fn build_template(&self, context: &mut #bevy_ecs::template::TemplateContext) -> #bevy_ecs::error::Result<Self::Output> {48#bevy_ecs::error::Result::Ok(#type_ident {49#(#template_field_builds,)*50})51}5253fn clone_template(&self) -> Self {54Self {55#(#template_field_clones,)*56}57}58}5960impl #impl_generics #FQDefault for #template_ident #type_generics #where_clause {61fn default() -> Self {62Self {63#(#template_field_defaults,)*64}65}66}67}68}69Fields::Unnamed(_) => {70quote! {71#[allow(missing_docs)]72#maybe_pub struct #template_ident #impl_generics (73#(#template_fields,)*74) #where_clause;7576impl #impl_generics #bevy_ecs::template::Template for #template_ident #type_generics #where_clause {77type Output = #type_ident #type_generics;78fn build_template(&self, context: &mut #bevy_ecs::template::TemplateContext) -> #bevy_ecs::error::Result<Self::Output> {79#bevy_ecs::error::Result::Ok(#type_ident (80#(#template_field_builds,)*81))82}8384fn clone_template(&self) -> Self {85Self(86#(#template_field_clones,)*87)88}89}9091impl #impl_generics #FQDefault for #template_ident #type_generics #where_clause {92fn default() -> Self {93Self (94#(#template_field_defaults,)*95)96}97}98}99}100Fields::Unit => {101quote! {102#[allow(missing_docs)]103#maybe_pub struct #template_ident;104105impl #impl_generics #bevy_ecs::template::Template for #template_ident #type_generics #where_clause {106type Output = #type_ident;107fn build_template(&self, context: &mut #bevy_ecs::template::TemplateContext) -> #bevy_ecs::error::Result<Self::Output> {108#bevy_ecs::error::Result::Ok(#type_ident)109}110111fn clone_template(&self) -> Self {112Self113}114}115116impl #impl_generics #FQDefault for #template_ident #type_generics #where_clause {117fn default() -> Self {118Self119}120}121}122}123}124}125Data::Enum(data_enum) => {126let mut variant_definitions = Vec::new();127let mut variant_builds = Vec::new();128let mut variant_clones = Vec::new();129let mut variant_default_ident = None;130let mut variant_defaults = Vec::new();131for variant in &data_enum.variants {132let result = match struct_impl(&variant.fields, &bevy_ecs, true) {133Ok(result) => result,134Err(err) => return err.into_compile_error().into(),135};136let StructImpl {137template_fields,138template_field_builds,139template_field_defaults,140template_field_clones,141..142} = result;143144let is_default = variant145.attrs146.iter()147.any(|a| a.path().is_ident(TEMPLATE_DEFAULT_ATTRIBUTE));148if is_default && variant_default_ident.is_some() {149panic!("Cannot have multiple default variants");150}151let variant_ident = &variant.ident;152let variant_name_lower = variant_ident.to_string().to_lowercase();153let variant_default_name = format_ident!("default_{}", variant_name_lower);154match &variant.fields {155Fields::Named(fields) => {156variant_definitions.push(quote! {157#variant_ident {158#(#template_fields,)*159}160});161let field_idents = fields.named.iter().map(|f| &f.ident);162variant_builds.push(quote! {163// TODO: proper assignments here164#template_ident::#variant_ident {165#(#field_idents,)*166} => {167#type_ident::#variant_ident {168#(#template_field_builds,)*169}170}171});172173let field_idents = fields.named.iter().map(|f| &f.ident);174variant_clones.push(quote! {175// TODO: proper assignments here176#template_ident::#variant_ident {177#(#field_idents,)*178} => {179#template_ident::#variant_ident {180#(#template_field_clones,)*181}182}183});184185if is_default {186variant_default_ident = Some(quote! {187Self::#variant_ident {188#(#template_field_defaults,)*189}190});191}192variant_defaults.push(quote! {193/// Default value for this variant, generated by a `FromTemplate` derive.194#maybe_pub fn #variant_default_name() -> Self {195Self::#variant_ident {196#(#template_field_defaults,)*197}198}199});200}201Fields::Unnamed(FieldsUnnamed { unnamed: f, .. }) => {202let field_idents = f203.iter()204.enumerate()205.map(|(i, _)| format_ident!("t{}", i))206.collect::<Vec<_>>();207variant_definitions.push(quote! {208#variant_ident(#(#template_fields,)*)209});210variant_builds.push(quote! {211// TODO: proper assignments here212#template_ident::#variant_ident(213#(#field_idents,)*214) => {215#type_ident::#variant_ident(216#(#template_field_builds,)*217)218}219});220variant_clones.push(quote! {221#template_ident::#variant_ident(222#(#field_idents,)*223) => {224#template_ident::#variant_ident(225#(#template_field_clones,)*226)227}228});229if is_default {230variant_default_ident = Some(quote! {231Self::#variant_ident(232#(#template_field_defaults,)*233)234});235}236237variant_defaults.push(quote! {238/// Default value for this variant, generated by a `FromTemplate` derive.239#maybe_pub fn #variant_default_name() -> Self {240Self::#variant_ident(241#(#template_field_defaults,)*242)243}244});245}246Fields::Unit => {247variant_definitions.push(quote! {#variant_ident});248variant_builds.push(249quote! {#template_ident::#variant_ident => #type_ident::#variant_ident},250);251variant_clones.push(252quote! {#template_ident::#variant_ident => #template_ident::#variant_ident},253);254if is_default {255variant_default_ident = Some(quote! {256Self::#variant_ident257});258}259variant_defaults.push(quote! {260/// Default value for this variant, generated by a `FromTemplate` derive.261#maybe_pub fn #variant_default_name() -> Self {262Self::#variant_ident263}264});265}266}267}268269if variant_default_ident.is_none() {270panic!("Deriving Template for enums requires picking a default variant using #[default]");271}272273quote! {274#[allow(missing_docs)]275#maybe_pub enum #template_ident #type_generics #where_clause {276#(#variant_definitions,)*277}278279impl #impl_generics #template_ident #type_generics #where_clause {280#(#variant_defaults)*281}282283impl #impl_generics #bevy_ecs::template::Template for #template_ident #type_generics #where_clause {284type Output = #type_ident #type_generics;285fn build_template(&self, context: &mut #bevy_ecs::template::TemplateContext) -> #bevy_ecs::error::Result<Self::Output> {286#bevy_ecs::error::Result::Ok(match self {287#(#variant_builds,)*288})289}290291fn clone_template(&self) -> Self {292match self {293#(#variant_clones,)*294}295}296}297298impl #impl_generics #FQDefault for #template_ident #type_generics #where_clause {299fn default() -> Self {300#variant_default_ident301}302}303}304}305Data::Union(_) => panic!("Union types are not supported yet."),306};307308let mut unpin_where_clause = where_clause.cloned().unwrap_or_else(|| WhereClause {309where_token: <Token![where]>::default(),310predicates: Punctuated::new(),311});312313unpin_where_clause314.predicates315.push(parse_quote! { for<'a> [()]: #bevy_ecs::template::SpecializeFromTemplate });316317TokenStream::from(quote! {318impl #impl_generics #bevy_ecs::template::FromTemplate for #type_ident #type_generics #where_clause {319type Template = #template_ident #type_generics;320}321322impl #impl_generics ::core::marker::Unpin for #type_ident #type_generics #unpin_where_clause {}323324#template325})326}327328struct StructImpl {329template_fields: Vec<proc_macro2::TokenStream>,330template_field_builds: Vec<proc_macro2::TokenStream>,331template_field_defaults: Vec<proc_macro2::TokenStream>,332template_field_clones: Vec<proc_macro2::TokenStream>,333}334335enum TemplateType {336FromTemplate,337BuiltIn,338Manual(Path),339}340341fn struct_impl(fields: &Fields, bevy_ecs: &Path, is_enum: bool) -> Result<StructImpl> {342let mut template_fields = Vec::with_capacity(fields.len());343let mut template_field_builds = Vec::with_capacity(fields.len());344let mut template_field_defaults = Vec::with_capacity(fields.len());345let mut template_field_clones = Vec::with_capacity(fields.len());346let is_named = matches!(fields, Fields::Named(_));347for (index, field) in fields.iter().enumerate() {348let is_pub = matches!(field.vis, syn::Visibility::Public(_));349let field_maybe_pub = if is_pub { quote!(pub) } else { quote!() };350let ident = &field.ident;351let ty = &field.ty;352let index = Index::from(index);353let mut template_type = TemplateType::FromTemplate;354for attr in &field.attrs {355if attr.path().is_ident(TEMPLATE_ATTRIBUTE) {356attr.parse_args_with(|stream: ParseStream| {357let forked = stream.fork();358let ident = forked.parse::<Ident>()?;359if ident == BUILT_IN_ATTRIBUTE {360stream.parse::<Ident>()?;361template_type = TemplateType::BuiltIn;362} else {363if let Ok(path) = stream.parse::<Path>() {364template_type = TemplateType::Manual(path);365} else {366return Err(syn::Error::new(367attr.span(),368"Expected a Template type path",369));370}371}372Ok(())373})?;374}375}376377let template_type = match template_type {378TemplateType::FromTemplate => {379quote!(<#ty as #bevy_ecs::template::FromTemplate>::Template)380}381TemplateType::BuiltIn => {382quote!(<#ty as #bevy_ecs::template::BuiltInTemplate>::Template)383}384TemplateType::Manual(path) => quote! {#path},385};386387if is_named {388template_fields.push(quote! {389#field_maybe_pub #ident: #template_type390});391if is_enum {392template_field_builds.push(quote! {393#ident: #ident.build_template(context)?394});395template_field_clones.push(quote! {396#ident: #bevy_ecs::template::Template::clone_template(#ident)397});398} else {399template_field_builds.push(quote! {400#ident: self.#ident.build_template(context)?401});402template_field_clones.push(quote! {403#ident: #bevy_ecs::template::Template::clone_template(&self.#ident)404});405}406407template_field_defaults.push(quote! {408#ident: #FQDefault::default()409});410} else {411template_fields.push(quote! {412#field_maybe_pub #template_type413});414if is_enum {415let enum_tuple_ident = format_ident!("t{}", index);416template_field_builds.push(quote! {417#enum_tuple_ident.build_template(context)?418});419template_field_clones.push(quote! {420#bevy_ecs::template::Template::clone_template(#enum_tuple_ident)421});422} else {423template_field_builds.push(quote! {424self.#index.build_template(context)?425});426template_field_clones.push(quote! {427#bevy_ecs::template::Template::clone_template(&self.#index)428});429}430template_field_defaults.push(quote! {431#FQDefault::default()432});433}434}435Ok(StructImpl {436template_fields,437template_field_builds,438template_field_defaults,439template_field_clones,440})441}442443444