//! General-purpose utility functions for internal usage within this crate. use crate::derive_data::{ReflectMeta, StructField}; use crate::field_attributes::ReflectIgnoreBehavior; use crate::fq_std::{FQAny, FQOption, FQSend, FQSync}; use bevy_macro_utils::BevyManifest; use bit_set::BitSet; use proc_macro2::{Ident, Span}; use quote::{quote, ToTokens}; use syn::{spanned::Spanned, LitStr, Member, Path, Type, WhereClause}; /// Returns the correct path for `bevy_reflect`. pub(crate) fn get_bevy_reflect_path() -> Path { BevyManifest::get_path_direct("bevy_reflect") } /// Returns the "reflected" ident for a given string. /// /// # Example /// /// ```ignore /// let reflected: Ident = get_reflect_ident("Hash"); /// assert_eq!("ReflectHash", reflected.to_string()); /// ``` pub(crate) fn get_reflect_ident(name: &str) -> Ident { let reflected = format!("Reflect{name}"); Ident::new(&reflected, Span::call_site()) } /// Helper struct used to process an iterator of `Result, syn::Error>`, /// combining errors into one along the way. pub(crate) struct ResultSifter { items: Vec, errors: Option, } /// Returns a [`Member`] made of `ident` or `index` if `ident` is None. /// /// Rust struct syntax allows for `Struct { foo: "string" }` with explicitly /// named fields. It allows the `Struct { 0: "string" }` syntax when the struct /// is declared as a tuple struct. /// /// ``` /// # fn main() { /// struct Foo { field: &'static str } /// struct Bar(&'static str); /// let Foo { field } = Foo { field: "hi" }; /// let Bar { 0: field } = Bar { 0: "hello" }; /// let Bar(field) = Bar("hello"); // more common syntax /// # } /// ``` /// /// This function helps field access in context where you are declaring either /// a tuple struct or a struct with named fields. If you don't have a field name, /// it means you need to access the struct through an index. pub(crate) fn ident_or_index(ident: Option<&Ident>, index: usize) -> Member { ident.map_or_else( || Member::Unnamed(index.into()), |ident| Member::Named(ident.clone()), ) } /// Options defining how to extend the `where` clause in reflection with any additional bounds needed. pub(crate) struct WhereClauseOptions { /// Type parameters that need extra trait bounds. parameter_types: Box<[Ident]>, /// Trait bounds to add to the type parameters. parameter_trait_bounds: Box<[proc_macro2::TokenStream]>, /// Any types that will be reflected and need an extra trait bound active_types: Box<[Type]>, /// Trait bounds to add to the active types active_trait_bounds: Box<[proc_macro2::TokenStream]>, /// Any types that won't be reflected and need an extra trait bound ignored_types: Box<[Type]>, /// Trait bounds to add to the ignored types ignored_trait_bounds: Box<[proc_macro2::TokenStream]>, } impl Default for WhereClauseOptions { /// By default, don't add any additional bounds to the `where` clause fn default() -> Self { Self { parameter_types: Box::new([]), active_types: Box::new([]), ignored_types: Box::new([]), active_trait_bounds: Box::new([]), ignored_trait_bounds: Box::new([]), parameter_trait_bounds: Box::new([]), } } } impl WhereClauseOptions { /// Create [`WhereClauseOptions`] for a struct or enum type. pub fn new<'a: 'b, 'b>( meta: &ReflectMeta, active_fields: impl Iterator>, ignored_fields: impl Iterator>, ) -> Self { Self::new_with_bounds(meta, active_fields, ignored_fields, |_| None, |_| None) } /// Create [`WhereClauseOptions`] for a simple value type. pub fn new_value(meta: &ReflectMeta) -> Self { Self::new_with_bounds( meta, std::iter::empty(), std::iter::empty(), |_| None, |_| None, ) } /// Create [`WhereClauseOptions`] for a struct or enum type. /// /// Compared to [`WhereClauseOptions::new`], this version allows you to specify /// custom trait bounds for each field. pub fn new_with_bounds<'a: 'b, 'b>( meta: &ReflectMeta, active_fields: impl Iterator>, ignored_fields: impl Iterator>, active_bounds: impl Fn(&StructField<'a>) -> Option, ignored_bounds: impl Fn(&StructField<'a>) -> Option, ) -> Self { let bevy_reflect_path = meta.bevy_reflect_path(); let is_from_reflect = meta.from_reflect().should_auto_derive(); let (active_types, active_trait_bounds): (Vec<_>, Vec<_>) = active_fields .map(|field| { let ty = field.data.ty.clone(); let custom_bounds = active_bounds(field).map(|bounds| quote!(+ #bounds)); let bounds = if is_from_reflect { quote!(#bevy_reflect_path::FromReflect #custom_bounds) } else { quote!(#bevy_reflect_path::Reflect #custom_bounds) }; (ty, bounds) }) .unzip(); let (ignored_types, ignored_trait_bounds): (Vec<_>, Vec<_>) = ignored_fields .map(|field| { let ty = field.data.ty.clone(); let custom_bounds = ignored_bounds(field).map(|bounds| quote!(+ #bounds)); let bounds = quote!(#FQAny + #FQSend + #FQSync #custom_bounds); (ty, bounds) }) .unzip(); let (parameter_types, parameter_trait_bounds): (Vec<_>, Vec<_>) = if meta.traits().type_path_attrs().should_auto_derive() { meta.type_path() .generics() .type_params() .map(|param| { let ident = param.ident.clone(); let bounds = quote!(#bevy_reflect_path::TypePath); (ident, bounds) }) .unzip() } else { // If we don't need to derive `TypePath` for the type parameters, // we can skip adding its bound to the `where` clause. (Vec::new(), Vec::new()) }; Self { active_types: active_types.into_boxed_slice(), active_trait_bounds: active_trait_bounds.into_boxed_slice(), ignored_types: ignored_types.into_boxed_slice(), ignored_trait_bounds: ignored_trait_bounds.into_boxed_slice(), parameter_types: parameter_types.into_boxed_slice(), parameter_trait_bounds: parameter_trait_bounds.into_boxed_slice(), } } } /// Extends the `where` clause in reflection with any additional bounds needed. /// /// This is mostly used to add additional bounds to reflected objects with generic types. /// For reflection purposes, we usually have: /// * `active_trait_bounds: Reflect` /// * `ignored_trait_bounds: Any + Send + Sync` /// /// # Arguments /// /// * `where_clause`: existing `where` clause present on the object to be derived /// * `where_clause_options`: additional parameters defining which trait bounds to add to the `where` clause /// /// # Example /// /// The struct: /// ```ignore /// #[derive(Reflect)] /// struct Foo { /// a: T, /// #[reflect(ignore)] /// b: U /// } /// ``` /// will have active types: `[T]` and ignored types: `[U]` /// /// The `extend_where_clause` function will yield the following `where` clause: /// ```ignore /// where /// T: Reflect, // active_trait_bounds /// U: Any + Send + Sync, // ignored_trait_bounds /// ``` pub(crate) fn extend_where_clause( where_clause: Option<&WhereClause>, where_clause_options: &WhereClauseOptions, ) -> proc_macro2::TokenStream { let parameter_types = &where_clause_options.parameter_types; let active_types = &where_clause_options.active_types; let ignored_types = &where_clause_options.ignored_types; let parameter_trait_bounds = &where_clause_options.parameter_trait_bounds; let active_trait_bounds = &where_clause_options.active_trait_bounds; let ignored_trait_bounds = &where_clause_options.ignored_trait_bounds; let mut generic_where_clause = if let Some(where_clause) = where_clause { let predicates = where_clause.predicates.iter(); quote! {where #(#predicates,)*} } else if !(parameter_types.is_empty() && active_types.is_empty() && ignored_types.is_empty()) { quote! {where} } else { quote!() }; // The nested parentheses here are required to properly scope HRTBs coming // from field types to the type itself, as the compiler will scope them to // the whole bound by default, resulting in a failure to prove trait // adherence. generic_where_clause.extend(quote! { #((#active_types): #active_trait_bounds,)* #((#ignored_types): #ignored_trait_bounds,)* // Leave parameter bounds to the end for more sane error messages. #((#parameter_types): #parameter_trait_bounds,)* }); generic_where_clause } impl Default for ResultSifter { fn default() -> Self { Self { items: Vec::new(), errors: None, } } } impl ResultSifter { /// Sift the given result, combining errors if necessary. pub fn sift(&mut self, result: Result) { match result { Ok(data) => self.items.push(data), Err(err) => { if let Some(ref mut errors) = self.errors { errors.combine(err); } else { self.errors = Some(err); } } } } /// Associated method that provides a convenient implementation for [`Iterator::fold`]. pub fn fold(mut sifter: Self, result: Result) -> Self { sifter.sift(result); sifter } /// Complete the sifting process and return the final result. pub fn finish(self) -> Result, syn::Error> { if let Some(errors) = self.errors { Err(errors) } else { Ok(self.items) } } } /// Converts an iterator over ignore behavior of members to a bitset of ignored members. /// /// Takes into account the fact that always ignored (non-reflected) members are skipped. /// /// # Example /// ```rust,ignore /// pub struct HelloWorld { /// reflected_field: u32 // index: 0 /// /// #[reflect(ignore)] /// non_reflected_field: u32 // index: N/A (not 1!) /// /// #[reflect(skip_serializing)] /// non_serialized_field: u32 // index: 1 /// } /// ``` /// Would convert to the `0b01` bitset (i.e second field is NOT serialized) /// pub(crate) fn members_to_serialization_denylist(member_iter: T) -> BitSet where T: Iterator, { let mut bitset = BitSet::default(); member_iter.fold(0, |next_idx, member| match member { ReflectIgnoreBehavior::IgnoreAlways => next_idx, ReflectIgnoreBehavior::IgnoreSerialization => { bitset.insert(next_idx); next_idx + 1 } ReflectIgnoreBehavior::None => next_idx + 1, }); bitset } /// Turns an `Option` into a `TokenStream` for an `Option`. pub(crate) fn wrap_in_option(tokens: Option) -> proc_macro2::TokenStream { match tokens { Some(tokens) => quote! { #FQOption::Some(#tokens) }, None => quote! { #FQOption::None }, } } /// Contains tokens representing different kinds of string. #[derive(Clone)] pub(crate) enum StringExpr { /// A string that is valid at compile time. /// /// This is either a string literal like `"mystring"`, /// or a string created by a macro like [`module_path`] /// or [`concat`]. Const(proc_macro2::TokenStream), /// A [string slice](str) that is borrowed for a `'static` lifetime. Borrowed(proc_macro2::TokenStream), /// An [owned string](String). Owned(proc_macro2::TokenStream), } impl From for StringExpr { fn from(value: T) -> Self { Self::from_lit(&LitStr::new(&value.to_string(), value.span())) } } impl StringExpr { /// Creates a [constant] [`StringExpr`] from a [`struct@LitStr`]. /// /// [constant]: StringExpr::Const pub fn from_lit(lit: &LitStr) -> Self { Self::Const(lit.to_token_stream()) } /// Creates a [constant] [`StringExpr`] by interpreting a [string slice][str] as a [`struct@LitStr`]. /// /// [constant]: StringExpr::Const pub fn from_str(string: &str) -> Self { Self::Const(string.into_token_stream()) } /// Returns tokens for an [owned string](String). /// /// The returned expression will allocate unless the [`StringExpr`] is [already owned]. /// /// [already owned]: StringExpr::Owned pub fn into_owned(self) -> proc_macro2::TokenStream { match self { Self::Const(tokens) | Self::Borrowed(tokens) => quote! { ::std::string::ToString::to_string(#tokens) }, Self::Owned(owned) => owned, } } /// Returns tokens for a statically borrowed [string slice](str). pub fn into_borrowed(self) -> proc_macro2::TokenStream { match self { Self::Const(tokens) | Self::Borrowed(tokens) => tokens, Self::Owned(owned) => quote! { &#owned }, } } /// Appends a [`StringExpr`] to another. /// /// If both expressions are [`StringExpr::Const`] this will use [`concat`] to merge them. pub fn appended_by(mut self, other: StringExpr) -> Self { if let Self::Const(tokens) = self { if let Self::Const(more) = other { return Self::Const(quote! { ::core::concat!(#tokens, #more) }); } self = Self::Const(tokens); } let owned = self.into_owned(); let borrowed = other.into_borrowed(); Self::Owned(quote! { #owned + #borrowed }) } } impl Default for StringExpr { fn default() -> Self { StringExpr::from_str("") } } impl FromIterator for StringExpr { fn from_iter>(iter: T) -> Self { let mut iter = iter.into_iter(); match iter.next() { Some(mut expr) => { for next in iter { expr = expr.appended_by(next); } expr } None => Default::default(), } } }