Path: blob/main/crates/bevy_ecs/macro_logic/src/map_entities.rs
30636 views
use proc_macro2::TokenStream;1use quote::{format_ident, quote, ToTokens};2use syn::{3parse::Parse, spanned::Spanned, Data, DataEnum, DataStruct, Expr, ExprPath, Ident, Member,4Path, Token,5};67use crate::component::relationship_field;89const ENTITIES: &str = "entities";1011/// Implements `MapEntities`12pub fn map_entities(13data: &Data,14bevy_ecs: &Path,15self_ident: Ident,16is_relationship: bool,17is_relationship_target: bool,18map_entities_attr: Option<MapEntitiesAttributeKind>,19) -> Option<TokenStream> {20if let Some(map_entities_override) = map_entities_attr {21let map_entities_tokens = map_entities_override.to_token_stream(bevy_ecs);22return Some(quote!(23#map_entities_tokens(#self_ident, mapper)24));25}2627match data {28Data::Struct(DataStruct { fields, .. }) => {29let mut map = Vec::with_capacity(fields.len());3031let relationship = if is_relationship || is_relationship_target {32relationship_field(fields, "MapEntities", fields.span()).ok()33} else {34None35};36fields37.iter()38.enumerate()39.filter(|(_, field)| {40field.attrs.iter().any(|a| a.path().is_ident(ENTITIES))41|| relationship.is_some_and(|relationship| relationship == *field)42})43.for_each(|(index, field)| {44let field_member = field45.ident46.clone()47.map_or(Member::from(index), Member::Named);4849map.push(quote!(#self_ident.#field_member.map_entities(mapper);));50});51if map.is_empty() {52return None;53};54Some(quote!(55#(#map)*56))57}58Data::Enum(DataEnum { variants, .. }) => {59let mut map = Vec::with_capacity(variants.len());6061for variant in variants.iter() {62let field_members = variant63.fields64.iter()65.enumerate()66.filter(|(_, field)| field.attrs.iter().any(|a| a.path().is_ident(ENTITIES)))67.map(|(index, field)| {68field69.ident70.clone()71.map_or(Member::from(index), Member::Named)72})73.collect::<Vec<_>>();7475let ident = &variant.ident;76let field_idents = field_members77.iter()78.map(|member| format_ident!("__self{}", member))79.collect::<Vec<_>>();8081map.push(82quote!(Self::#ident {#(#field_members: #field_idents,)* ..} => {83#(#field_idents.map_entities(mapper);)*84}),85);86}8788if map.is_empty() {89return None;90};9192Some(quote!(93match #self_ident {94#(#map,)*95_ => {}96}97))98}99Data::Union(_) => None,100}101}102103/// The type of `MapEntities` attribute.104#[derive(Debug)]105pub enum MapEntitiesAttributeKind {106/// expressions like function or struct names107///108/// structs will throw compile errors on the code generation so this is safe109Path(ExprPath),110/// When no value is specified111Default,112}113114impl MapEntitiesAttributeKind {115fn from_expr(value: Expr) -> syn::Result<Self> {116match value {117Expr::Path(path) => Ok(Self::Path(path)),118// throw meaningful error on all other expressions119_ => Err(syn::Error::new(120value.span(),121[122"Not supported in this position, please use one of the following:",123"- path to function",124"- nothing to default to MapEntities implementation",125]126.join("\n"),127)),128}129}130131fn to_token_stream(&self, bevy_ecs_path: &Path) -> TokenStream {132match self {133MapEntitiesAttributeKind::Path(path) => path.to_token_stream(),134MapEntitiesAttributeKind::Default => {135quote!(136<Self as #bevy_ecs_path::entity::MapEntities>::map_entities137)138}139}140}141}142143impl Parse for MapEntitiesAttributeKind {144fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {145if input.peek(Token![=]) {146input.parse::<Token![=]>()?;147input.parse::<Expr>().and_then(Self::from_expr)148} else {149Ok(Self::Default)150}151}152}153154155