mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 04:33:37 +00:00
split bevy_reflect::derive::utilities
into proper modules (#15354)
# Objective - A utilities module is considered to be a bad practice and poor organization of code, so this fixes it. ## Solution - Split each struct into its own module - Move related lose functions into their own module - Move the last few bits into good places ## Testing - CI --------- Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
This commit is contained in:
parent
59c0521690
commit
67615c5051
25 changed files with 498 additions and 497 deletions
|
@ -20,10 +20,9 @@ functions = []
|
|||
|
||||
[dependencies]
|
||||
bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.15.0-dev" }
|
||||
|
||||
syn = { version = "2.0", features = ["full"] }
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
syn = { version = "2.0", features = ["full"] }
|
||||
uuid = { version = "1.1", features = ["v4"] }
|
||||
|
||||
[lints]
|
||||
|
|
36
crates/bevy_reflect/derive/src/attribute_parser.rs
Normal file
36
crates/bevy_reflect/derive/src/attribute_parser.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use syn::parse::{Parse, ParseStream, Peek};
|
||||
use syn::punctuated::Punctuated;
|
||||
|
||||
/// Returns a [`syn::parse::Parser`] which parses a stream of zero or more occurrences of `T`
|
||||
/// separated by punctuation of type `P`, with optional trailing punctuation.
|
||||
///
|
||||
/// This is functionally the same as [`Punctuated::parse_terminated`],
|
||||
/// but accepts a closure rather than a function pointer.
|
||||
pub(crate) fn terminated_parser<T, P, F: FnMut(ParseStream) -> syn::Result<T>>(
|
||||
terminator: P,
|
||||
mut parser: F,
|
||||
) -> impl FnOnce(ParseStream) -> syn::Result<Punctuated<T, P::Token>>
|
||||
where
|
||||
P: Peek,
|
||||
P::Token: Parse,
|
||||
{
|
||||
let _ = terminator;
|
||||
move |stream: ParseStream| {
|
||||
let mut punctuated = Punctuated::new();
|
||||
|
||||
loop {
|
||||
if stream.is_empty() {
|
||||
break;
|
||||
}
|
||||
let value = parser(stream)?;
|
||||
punctuated.push_value(value);
|
||||
if stream.is_empty() {
|
||||
break;
|
||||
}
|
||||
let punct = stream.parse()?;
|
||||
punctuated.push_punct(punct);
|
||||
}
|
||||
|
||||
Ok(punctuated)
|
||||
}
|
||||
}
|
|
@ -5,10 +5,9 @@
|
|||
//! the derive helper attribute for `Reflect`, which looks like:
|
||||
//! `#[reflect(PartialEq, Default, ...)]` and `#[reflect_value(PartialEq, Default, ...)]`.
|
||||
|
||||
use crate::attribute_parser::terminated_parser;
|
||||
use crate::custom_attributes::CustomAttributes;
|
||||
use crate::derive_data::ReflectTraitToImpl;
|
||||
use crate::utility;
|
||||
use crate::utility::terminated_parser;
|
||||
use bevy_macro_utils::fq_std::{FQAny, FQOption};
|
||||
use proc_macro2::{Ident, Span};
|
||||
use quote::quote_spanned;
|
||||
|
@ -268,7 +267,7 @@ impl ContainerAttributes {
|
|||
let ident_name = ident.to_string();
|
||||
|
||||
// Create the reflect ident
|
||||
let mut reflect_ident = utility::get_reflect_ident(&ident_name);
|
||||
let mut reflect_ident = crate::ident::get_reflect_ident(&ident_name);
|
||||
// We set the span to the old ident so any compile errors point to that ident instead
|
||||
reflect_ident.set_span(ident.span());
|
||||
|
||||
|
|
|
@ -3,15 +3,17 @@ use proc_macro2::Span;
|
|||
|
||||
use crate::container_attributes::{ContainerAttributes, FromReflectAttrs, TypePathAttrs};
|
||||
use crate::field_attributes::FieldAttributes;
|
||||
use crate::result_sifter::ResultSifter;
|
||||
use crate::string_expr::StringExpr;
|
||||
use crate::type_path::parse_path_no_leading_colon;
|
||||
use crate::utility::{StringExpr, WhereClauseOptions};
|
||||
use crate::where_clause_options::WhereClauseOptions;
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::token::Comma;
|
||||
|
||||
use crate::remote::RemoteType;
|
||||
use crate::serialization::SerializationDataDef;
|
||||
use crate::{
|
||||
utility, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME, TYPE_NAME_ATTRIBUTE_NAME,
|
||||
REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME, TYPE_NAME_ATTRIBUTE_NAME,
|
||||
TYPE_PATH_ATTRIBUTE_NAME,
|
||||
};
|
||||
use syn::punctuated::Punctuated;
|
||||
|
@ -399,7 +401,7 @@ impl<'a> ReflectDerive<'a> {
|
|||
|
||||
fn collect_struct_fields(fields: &'a Fields) -> Result<Vec<StructField<'a>>, syn::Error> {
|
||||
let mut active_index = 0;
|
||||
let sifter: utility::ResultSifter<StructField<'a>> = fields
|
||||
let sifter: ResultSifter<StructField<'a>> = fields
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(
|
||||
|
@ -423,10 +425,7 @@ impl<'a> ReflectDerive<'a> {
|
|||
})
|
||||
},
|
||||
)
|
||||
.fold(
|
||||
utility::ResultSifter::default(),
|
||||
utility::ResultSifter::fold,
|
||||
);
|
||||
.fold(ResultSifter::default(), ResultSifter::fold);
|
||||
|
||||
sifter.finish()
|
||||
}
|
||||
|
@ -434,7 +433,7 @@ impl<'a> ReflectDerive<'a> {
|
|||
fn collect_enum_variants(
|
||||
variants: &'a Punctuated<Variant, Comma>,
|
||||
) -> Result<Vec<EnumVariant<'a>>, syn::Error> {
|
||||
let sifter: utility::ResultSifter<EnumVariant<'a>> = variants
|
||||
let sifter: ResultSifter<EnumVariant<'a>> = variants
|
||||
.iter()
|
||||
.map(|variant| -> Result<EnumVariant, syn::Error> {
|
||||
let fields = Self::collect_struct_fields(&variant.fields)?;
|
||||
|
@ -452,10 +451,7 @@ impl<'a> ReflectDerive<'a> {
|
|||
doc: crate::documentation::Documentation::from_attributes(&variant.attrs),
|
||||
})
|
||||
})
|
||||
.fold(
|
||||
utility::ResultSifter::default(),
|
||||
utility::ResultSifter::fold,
|
||||
);
|
||||
.fold(ResultSifter::default(), ResultSifter::fold);
|
||||
|
||||
sifter.finish()
|
||||
}
|
||||
|
@ -467,7 +463,7 @@ impl<'a> ReflectMeta<'a> {
|
|||
attrs,
|
||||
type_path,
|
||||
remote_ty: None,
|
||||
bevy_reflect_path: utility::get_bevy_reflect_path(),
|
||||
bevy_reflect_path: crate::meta::get_bevy_reflect_path(),
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: Default::default(),
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::derive_data::StructField;
|
||||
use crate::field_attributes::DefaultBehavior;
|
||||
use crate::{derive_data::ReflectEnum, utility::ident_or_index};
|
||||
use crate::{derive_data::ReflectEnum, ident::ident_or_index};
|
||||
use bevy_macro_utils::fq_std::{FQDefault, FQOption};
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
//! as opposed to an entire struct or enum. An example of such an attribute is
|
||||
//! the derive helper attribute for `Reflect`, which looks like: `#[reflect(ignore)]`.
|
||||
|
||||
use crate::attribute_parser::terminated_parser;
|
||||
use crate::custom_attributes::CustomAttributes;
|
||||
use crate::utility::terminated_parser;
|
||||
use crate::REFLECT_ATTRIBUTE_NAME;
|
||||
use quote::ToTokens;
|
||||
use syn::parse::ParseStream;
|
||||
|
|
|
@ -2,7 +2,8 @@ use crate::container_attributes::REFLECT_DEFAULT;
|
|||
use crate::derive_data::ReflectEnum;
|
||||
use crate::enum_utility::{EnumVariantOutputData, FromReflectVariantBuilder, VariantBuilder};
|
||||
use crate::field_attributes::DefaultBehavior;
|
||||
use crate::utility::{ident_or_index, WhereClauseOptions};
|
||||
use crate::ident::ident_or_index;
|
||||
use crate::where_clause_options::WhereClauseOptions;
|
||||
use crate::{ReflectMeta, ReflectStruct};
|
||||
use bevy_macro_utils::fq_std::{FQClone, FQDefault, FQOption};
|
||||
use proc_macro2::Span;
|
||||
|
|
45
crates/bevy_reflect/derive/src/ident.rs
Normal file
45
crates/bevy_reflect/derive/src/ident.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
use proc_macro2::{Ident, Span};
|
||||
use syn::Member;
|
||||
|
||||
/// Returns the "reflected" ident for a given string.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use proc_macro2::Ident;
|
||||
/// # // We can't import this method because of its visibility.
|
||||
/// # fn get_reflect_ident(name: &str) -> Ident {
|
||||
/// # let reflected = format!("Reflect{name}");
|
||||
/// # Ident::new(&reflected, proc_macro2::Span::call_site())
|
||||
/// # }
|
||||
/// 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())
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// ```
|
||||
/// 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 contexts where you are declaring either
|
||||
/// a tuple struct or a struct with named fields. If you don't have a field name,
|
||||
/// it means that you must access the field 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()),
|
||||
)
|
||||
}
|
|
@ -2,7 +2,7 @@ use bevy_macro_utils::fq_std::{FQAny, FQBox, FQOption, FQResult};
|
|||
|
||||
use quote::quote;
|
||||
|
||||
use crate::{derive_data::ReflectMeta, utility::WhereClauseOptions};
|
||||
use crate::{derive_data::ReflectMeta, where_clause_options::WhereClauseOptions};
|
||||
|
||||
pub fn impl_full_reflect(
|
||||
meta: &ReflectMeta,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::derive_data::ReflectMeta;
|
||||
use crate::utility::WhereClauseOptions;
|
||||
use crate::where_clause_options::WhereClauseOptions;
|
||||
use bevy_macro_utils::fq_std::FQResult;
|
||||
use quote::quote;
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::derive_data::ReflectMeta;
|
|||
use crate::impls::func::from_arg::impl_from_arg;
|
||||
use crate::impls::func::get_ownership::impl_get_ownership;
|
||||
use crate::impls::func::into_return::impl_into_return;
|
||||
use crate::utility::WhereClauseOptions;
|
||||
use crate::where_clause_options::WhereClauseOptions;
|
||||
use quote::quote;
|
||||
|
||||
pub(crate) fn impl_function_traits(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::derive_data::ReflectMeta;
|
||||
use crate::utility::WhereClauseOptions;
|
||||
use crate::where_clause_options::WhereClauseOptions;
|
||||
use quote::quote;
|
||||
|
||||
pub(crate) fn impl_get_ownership(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::derive_data::ReflectMeta;
|
||||
use crate::utility::WhereClauseOptions;
|
||||
use crate::where_clause_options::WhereClauseOptions;
|
||||
use quote::quote;
|
||||
|
||||
pub(crate) fn impl_into_return(
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
use crate::utility::{StringExpr, WhereClauseOptions};
|
||||
use crate::derive_data::{ReflectMeta, ReflectTypePath};
|
||||
use crate::string_expr::StringExpr;
|
||||
use crate::where_clause_options::WhereClauseOptions;
|
||||
use bevy_macro_utils::fq_std::FQOption;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, ToTokens};
|
||||
|
||||
use crate::{
|
||||
derive_data::{ReflectMeta, ReflectTypePath},
|
||||
utility::wrap_in_option,
|
||||
};
|
||||
|
||||
/// Returns an expression for a `NonGenericTypeCell` or `GenericTypeCell` to generate `'static` references.
|
||||
fn static_type_cell(
|
||||
meta: &ReflectMeta,
|
||||
property: TypedProperty,
|
||||
generator: proc_macro2::TokenStream,
|
||||
) -> proc_macro2::TokenStream {
|
||||
generator: TokenStream,
|
||||
) -> TokenStream {
|
||||
let bevy_reflect_path = meta.bevy_reflect_path();
|
||||
if meta.type_path().impl_is_generic() {
|
||||
let cell_type = match property {
|
||||
|
@ -48,11 +47,11 @@ pub(crate) enum TypedProperty {
|
|||
TypePath,
|
||||
}
|
||||
|
||||
pub(crate) fn impl_type_path(meta: &ReflectMeta) -> proc_macro2::TokenStream {
|
||||
pub(crate) fn impl_type_path(meta: &ReflectMeta) -> TokenStream {
|
||||
let where_clause_options = WhereClauseOptions::new(meta);
|
||||
|
||||
if !meta.attrs().type_path_attrs().should_auto_derive() {
|
||||
return proc_macro2::TokenStream::new();
|
||||
return TokenStream::new();
|
||||
}
|
||||
|
||||
let type_path = meta.type_path();
|
||||
|
@ -132,8 +131,8 @@ pub(crate) fn impl_type_path(meta: &ReflectMeta) -> proc_macro2::TokenStream {
|
|||
pub(crate) fn impl_typed(
|
||||
meta: &ReflectMeta,
|
||||
where_clause_options: &WhereClauseOptions,
|
||||
type_info_generator: proc_macro2::TokenStream,
|
||||
) -> proc_macro2::TokenStream {
|
||||
type_info_generator: TokenStream,
|
||||
) -> TokenStream {
|
||||
let type_path = meta.type_path();
|
||||
let bevy_reflect_path = meta.bevy_reflect_path();
|
||||
|
||||
|
@ -151,3 +150,15 @@ pub(crate) fn impl_typed(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Turns an `Option<TokenStream>` into a `TokenStream` for an `Option`.
|
||||
fn wrap_in_option(tokens: Option<TokenStream>) -> TokenStream {
|
||||
match tokens {
|
||||
Some(tokens) => quote! {
|
||||
#FQOption::Some(#tokens)
|
||||
},
|
||||
None => quote! {
|
||||
#FQOption::None
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::impls::{common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed};
|
||||
use crate::utility::WhereClauseOptions;
|
||||
use crate::where_clause_options::WhereClauseOptions;
|
||||
use crate::ReflectMeta;
|
||||
use bevy_macro_utils::fq_std::{FQBox, FQClone, FQOption, FQResult};
|
||||
use quote::quote;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
extern crate proc_macro;
|
||||
|
||||
mod attribute_parser;
|
||||
mod container_attributes;
|
||||
mod custom_attributes;
|
||||
mod derive_data;
|
||||
|
@ -24,15 +25,19 @@ mod documentation;
|
|||
mod enum_utility;
|
||||
mod field_attributes;
|
||||
mod from_reflect;
|
||||
mod ident;
|
||||
mod impls;
|
||||
mod meta;
|
||||
mod reflect_value;
|
||||
mod registration;
|
||||
mod remote;
|
||||
mod result_sifter;
|
||||
mod serialization;
|
||||
mod string_expr;
|
||||
mod struct_utility;
|
||||
mod trait_reflection;
|
||||
mod type_path;
|
||||
mod utility;
|
||||
mod where_clause_options;
|
||||
|
||||
use crate::derive_data::{ReflectDerive, ReflectMeta, ReflectStruct};
|
||||
use container_attributes::ContainerAttributes;
|
||||
|
|
7
crates/bevy_reflect/derive/src/meta.rs
Normal file
7
crates/bevy_reflect/derive/src/meta.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
use bevy_macro_utils::BevyManifest;
|
||||
use syn::Path;
|
||||
|
||||
/// Returns the correct path for `bevy_reflect`.
|
||||
pub(crate) fn get_bevy_reflect_path() -> Path {
|
||||
BevyManifest::get_path_direct("bevy_reflect")
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use crate::derive_data::ReflectMeta;
|
||||
use crate::serialization::SerializationDataDef;
|
||||
use crate::utility::WhereClauseOptions;
|
||||
use crate::where_clause_options::WhereClauseOptions;
|
||||
use quote::quote;
|
||||
use syn::Type;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::derive_data::{ReflectImplSource, ReflectProvenance, ReflectTraitToImpl};
|
||||
use crate::ident::ident_or_index;
|
||||
use crate::impls::impl_assertions;
|
||||
use crate::utility::ident_or_index;
|
||||
use crate::{
|
||||
from_reflect, impls, ReflectDerive, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME,
|
||||
};
|
||||
|
|
46
crates/bevy_reflect/derive/src/result_sifter.rs
Normal file
46
crates/bevy_reflect/derive/src/result_sifter.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
/// Helper struct used to process an iterator of `Result<Vec<T>, syn::Error>`,
|
||||
/// combining errors into one along the way.
|
||||
pub(crate) struct ResultSifter<T> {
|
||||
items: Vec<T>,
|
||||
errors: Option<syn::Error>,
|
||||
}
|
||||
|
||||
impl<T> Default for ResultSifter<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
items: Vec::new(),
|
||||
errors: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ResultSifter<T> {
|
||||
/// Sift the given result, combining errors if necessary.
|
||||
pub fn sift(&mut self, result: Result<T, syn::Error>) {
|
||||
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<T, syn::Error>) -> Self {
|
||||
sifter.sift(result);
|
||||
sifter
|
||||
}
|
||||
|
||||
/// Complete the sifting process and return the final result.
|
||||
pub fn finish(self) -> Result<Vec<T>, syn::Error> {
|
||||
if let Some(errors) = self.errors {
|
||||
Err(errors)
|
||||
} else {
|
||||
Ok(self.items)
|
||||
}
|
||||
}
|
||||
}
|
106
crates/bevy_reflect/derive/src/string_expr.rs
Normal file
106
crates/bevy_reflect/derive/src/string_expr.rs
Normal file
|
@ -0,0 +1,106 @@
|
|||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::{spanned::Spanned, LitStr};
|
||||
|
||||
/// 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(TokenStream),
|
||||
/// A [string slice](str) that is borrowed for a `'static` lifetime.
|
||||
Borrowed(TokenStream),
|
||||
/// An [owned string](String).
|
||||
Owned(TokenStream),
|
||||
}
|
||||
|
||||
impl<T: ToString + Spanned> From<T> 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) -> 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) -> 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<StringExpr> for StringExpr {
|
||||
fn from_iter<T: IntoIterator<Item = StringExpr>>(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(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
use crate::derive_data::StructField;
|
||||
use crate::{utility, ReflectStruct};
|
||||
use crate::ReflectStruct;
|
||||
use quote::quote;
|
||||
|
||||
/// A helper struct for creating remote-aware field accessors.
|
||||
|
@ -65,8 +65,10 @@ impl FieldAccessors {
|
|||
reflect_struct
|
||||
.active_fields()
|
||||
.map(|field| {
|
||||
let member =
|
||||
utility::ident_or_index(field.data.ident.as_ref(), field.declaration_index);
|
||||
let member = crate::ident::ident_or_index(
|
||||
field.data.ident.as_ref(),
|
||||
field.declaration_index,
|
||||
);
|
||||
let accessor = if is_remote {
|
||||
quote!(self.0.#member)
|
||||
} else {
|
||||
|
|
|
@ -33,7 +33,7 @@ pub(crate) fn reflect_trait(_args: &TokenStream, input: TokenStream) -> TokenStr
|
|||
let item_trait = &trait_info.item_trait;
|
||||
let trait_ident = &item_trait.ident;
|
||||
let trait_vis = &item_trait.vis;
|
||||
let reflect_trait_ident = crate::utility::get_reflect_ident(&item_trait.ident.to_string());
|
||||
let reflect_trait_ident = crate::ident::get_reflect_ident(&item_trait.ident.to_string());
|
||||
let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect");
|
||||
|
||||
let struct_doc = format!(
|
||||
|
|
|
@ -1,451 +0,0 @@
|
|||
//! General-purpose utility functions for internal usage within this crate.
|
||||
|
||||
use crate::derive_data::ReflectMeta;
|
||||
use bevy_macro_utils::{
|
||||
fq_std::{FQAny, FQOption, FQSend, FQSync},
|
||||
BevyManifest,
|
||||
};
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::parse::{Parse, ParseStream, Peek};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::{spanned::Spanned, LitStr, Member, Path, Token, 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
|
||||
///
|
||||
/// ```
|
||||
/// # use proc_macro2::Ident;
|
||||
/// # // We can't import this method because of its visibility.
|
||||
/// # fn get_reflect_ident(name: &str) -> Ident {
|
||||
/// # let reflected = format!("Reflect{name}");
|
||||
/// # Ident::new(&reflected, proc_macro2::Span::call_site())
|
||||
/// # }
|
||||
/// 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<Vec<T>, syn::Error>`,
|
||||
/// combining errors into one along the way.
|
||||
pub(crate) struct ResultSifter<T> {
|
||||
items: Vec<T>,
|
||||
errors: Option<syn::Error>,
|
||||
}
|
||||
|
||||
/// 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 for reflection.
|
||||
pub(crate) struct WhereClauseOptions<'a, 'b> {
|
||||
meta: &'a ReflectMeta<'b>,
|
||||
active_fields: Box<[Type]>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> WhereClauseOptions<'a, 'b> {
|
||||
pub fn new(meta: &'a ReflectMeta<'b>) -> Self {
|
||||
Self {
|
||||
meta,
|
||||
active_fields: Box::new([]),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_fields(meta: &'a ReflectMeta<'b>, active_fields: Box<[Type]>) -> Self {
|
||||
Self {
|
||||
meta,
|
||||
active_fields,
|
||||
}
|
||||
}
|
||||
|
||||
/// Extends the `where` clause for a type with additional bounds needed for the reflection impls.
|
||||
///
|
||||
/// The default bounds added are as follows:
|
||||
/// - `Self` has the bounds `Any + Send + Sync`
|
||||
/// - Type parameters have the bound `TypePath` unless `#[reflect(type_path = false)]` is present
|
||||
/// - Active fields have the bounds `TypePath` and either `PartialReflect` if `#[reflect(from_reflect = false)]` is present
|
||||
/// or `FromReflect` otherwise (or no bounds at all if `#[reflect(no_field_bounds)]` is present)
|
||||
///
|
||||
/// When the derive is used with `#[reflect(where)]`, the bounds specified in the attribute are added as well.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```ignore (bevy_reflect is not accessible from this crate)
|
||||
/// #[derive(Reflect)]
|
||||
/// struct Foo<T, U> {
|
||||
/// a: T,
|
||||
/// #[reflect(ignore)]
|
||||
/// b: U
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Generates the following where clause:
|
||||
///
|
||||
/// ```ignore (bevy_reflect is not accessible from this crate)
|
||||
/// where
|
||||
/// // `Self` bounds:
|
||||
/// Self: Any + Send + Sync,
|
||||
/// // Type parameter bounds:
|
||||
/// T: TypePath,
|
||||
/// U: TypePath,
|
||||
/// // Field bounds
|
||||
/// T: FromReflect + TypePath,
|
||||
/// ```
|
||||
///
|
||||
/// If we had added `#[reflect(where T: MyTrait)]` to the type, it would instead generate:
|
||||
///
|
||||
/// ```ignore (bevy_reflect is not accessible from this crate)
|
||||
/// where
|
||||
/// // `Self` bounds:
|
||||
/// Self: Any + Send + Sync,
|
||||
/// // Type parameter bounds:
|
||||
/// T: TypePath,
|
||||
/// U: TypePath,
|
||||
/// // Field bounds
|
||||
/// T: FromReflect + TypePath,
|
||||
/// // Custom bounds
|
||||
/// T: MyTrait,
|
||||
/// ```
|
||||
///
|
||||
/// And if we also added `#[reflect(no_field_bounds)]` to the type, it would instead generate:
|
||||
///
|
||||
/// ```ignore (bevy_reflect is not accessible from this crate)
|
||||
/// where
|
||||
/// // `Self` bounds:
|
||||
/// Self: Any + Send + Sync,
|
||||
/// // Type parameter bounds:
|
||||
/// T: TypePath,
|
||||
/// U: TypePath,
|
||||
/// // Custom bounds
|
||||
/// T: MyTrait,
|
||||
/// ```
|
||||
pub fn extend_where_clause(&self, where_clause: Option<&WhereClause>) -> TokenStream {
|
||||
// We would normally just use `Self`, but that won't work for generating things like assertion functions
|
||||
// and trait impls for a type's reference (e.g. `impl FromArg for &MyType`)
|
||||
let this = self.meta.type_path().true_type();
|
||||
|
||||
let required_bounds = self.required_bounds();
|
||||
|
||||
// Maintain existing where clause, if any.
|
||||
let mut generic_where_clause = if let Some(where_clause) = where_clause {
|
||||
let predicates = where_clause.predicates.iter();
|
||||
quote! {where #this: #required_bounds, #(#predicates,)*}
|
||||
} else {
|
||||
quote!(where #this: #required_bounds,)
|
||||
};
|
||||
|
||||
// Add additional reflection trait bounds
|
||||
let predicates = self.predicates();
|
||||
generic_where_clause.extend(quote! {
|
||||
#predicates
|
||||
});
|
||||
|
||||
generic_where_clause
|
||||
}
|
||||
|
||||
/// Returns an iterator the where clause predicates to extended the where clause with.
|
||||
fn predicates(&self) -> Punctuated<TokenStream, Token![,]> {
|
||||
let mut predicates = Punctuated::new();
|
||||
|
||||
if let Some(type_param_predicates) = self.type_param_predicates() {
|
||||
predicates.extend(type_param_predicates);
|
||||
}
|
||||
|
||||
if let Some(field_predicates) = self.active_field_predicates() {
|
||||
predicates.extend(field_predicates);
|
||||
}
|
||||
|
||||
if let Some(custom_where) = self.meta.attrs().custom_where() {
|
||||
predicates.push(custom_where.predicates.to_token_stream());
|
||||
}
|
||||
|
||||
predicates
|
||||
}
|
||||
|
||||
/// Returns an iterator over the where clause predicates for the type parameters
|
||||
/// if they require one.
|
||||
fn type_param_predicates(&self) -> Option<impl Iterator<Item = TokenStream> + '_> {
|
||||
self.type_path_bound().map(|type_path_bound| {
|
||||
self.meta
|
||||
.type_path()
|
||||
.generics()
|
||||
.type_params()
|
||||
.map(move |param| {
|
||||
let ident = ¶m.ident;
|
||||
|
||||
quote!(#ident : #type_path_bound)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns an iterator over the where clause predicates for the active fields.
|
||||
fn active_field_predicates(&self) -> Option<impl Iterator<Item = TokenStream> + '_> {
|
||||
if self.meta.attrs().no_field_bounds() {
|
||||
None
|
||||
} else {
|
||||
let bevy_reflect_path = self.meta.bevy_reflect_path();
|
||||
let reflect_bound = self.reflect_bound();
|
||||
|
||||
// `TypePath` is always required for active fields since they are used to
|
||||
// construct `NamedField` and `UnnamedField` instances for the `Typed` impl.
|
||||
// Likewise, `GetTypeRegistration` is always required for active fields since
|
||||
// they are used to register the type's dependencies.
|
||||
Some(self.active_fields.iter().map(move |ty| {
|
||||
quote!(
|
||||
#ty : #reflect_bound
|
||||
+ #bevy_reflect_path::TypePath
|
||||
// Needed for `Typed` impls
|
||||
+ #bevy_reflect_path::MaybeTyped
|
||||
// Needed for `GetTypeRegistration` impls
|
||||
+ #bevy_reflect_path::__macro_exports::RegisterForReflection
|
||||
)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// The `PartialReflect` or `FromReflect` bound to use based on `#[reflect(from_reflect = false)]`.
|
||||
fn reflect_bound(&self) -> TokenStream {
|
||||
let bevy_reflect_path = self.meta.bevy_reflect_path();
|
||||
|
||||
if self.meta.from_reflect().should_auto_derive() {
|
||||
quote!(#bevy_reflect_path::FromReflect)
|
||||
} else {
|
||||
quote!(#bevy_reflect_path::PartialReflect)
|
||||
}
|
||||
}
|
||||
|
||||
/// The `TypePath` bounds to use based on `#[reflect(type_path = false)]`.
|
||||
fn type_path_bound(&self) -> Option<TokenStream> {
|
||||
if self.meta.type_path_attrs().should_auto_derive() {
|
||||
let bevy_reflect_path = self.meta.bevy_reflect_path();
|
||||
Some(quote!(#bevy_reflect_path::TypePath))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// The minimum required bounds for a type to be reflected.
|
||||
fn required_bounds(&self) -> TokenStream {
|
||||
quote!(#FQAny + #FQSend + #FQSync)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for ResultSifter<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
items: Vec::new(),
|
||||
errors: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ResultSifter<T> {
|
||||
/// Sift the given result, combining errors if necessary.
|
||||
pub fn sift(&mut self, result: Result<T, syn::Error>) {
|
||||
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<T, syn::Error>) -> Self {
|
||||
sifter.sift(result);
|
||||
sifter
|
||||
}
|
||||
|
||||
/// Complete the sifting process and return the final result.
|
||||
pub fn finish(self) -> Result<Vec<T>, syn::Error> {
|
||||
if let Some(errors) = self.errors {
|
||||
Err(errors)
|
||||
} else {
|
||||
Ok(self.items)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Turns an `Option<TokenStream>` into a `TokenStream` for an `Option`.
|
||||
pub(crate) fn wrap_in_option(tokens: Option<TokenStream>) -> 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(TokenStream),
|
||||
/// A [string slice](str) that is borrowed for a `'static` lifetime.
|
||||
Borrowed(TokenStream),
|
||||
/// An [owned string](String).
|
||||
Owned(TokenStream),
|
||||
}
|
||||
|
||||
impl<T: ToString + Spanned> From<T> 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) -> 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) -> 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<StringExpr> for StringExpr {
|
||||
fn from_iter<T: IntoIterator<Item = StringExpr>>(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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a [`syn::parse::Parser`] which parses a stream of zero or more occurrences of `T`
|
||||
/// separated by punctuation of type `P`, with optional trailing punctuation.
|
||||
///
|
||||
/// This is functionally the same as [`Punctuated::parse_terminated`],
|
||||
/// but accepts a closure rather than a function pointer.
|
||||
pub(crate) fn terminated_parser<T, P, F: FnMut(ParseStream) -> syn::Result<T>>(
|
||||
terminator: P,
|
||||
mut parser: F,
|
||||
) -> impl FnOnce(ParseStream) -> syn::Result<Punctuated<T, P::Token>>
|
||||
where
|
||||
P: Peek,
|
||||
P::Token: Parse,
|
||||
{
|
||||
let _ = terminator;
|
||||
move |stream: ParseStream| {
|
||||
let mut punctuated = Punctuated::new();
|
||||
|
||||
loop {
|
||||
if stream.is_empty() {
|
||||
break;
|
||||
}
|
||||
let value = parser(stream)?;
|
||||
punctuated.push_value(value);
|
||||
if stream.is_empty() {
|
||||
break;
|
||||
}
|
||||
let punct = stream.parse()?;
|
||||
punctuated.push_punct(punct);
|
||||
}
|
||||
|
||||
Ok(punctuated)
|
||||
}
|
||||
}
|
199
crates/bevy_reflect/derive/src/where_clause_options.rs
Normal file
199
crates/bevy_reflect/derive/src/where_clause_options.rs
Normal file
|
@ -0,0 +1,199 @@
|
|||
use crate::derive_data::ReflectMeta;
|
||||
use bevy_macro_utils::fq_std::{FQAny, FQSend, FQSync};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::{Token, Type, WhereClause};
|
||||
|
||||
/// Options defining how to extend the `where` clause for reflection.
|
||||
pub(crate) struct WhereClauseOptions<'a, 'b> {
|
||||
meta: &'a ReflectMeta<'b>,
|
||||
active_fields: Box<[Type]>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> WhereClauseOptions<'a, 'b> {
|
||||
pub fn new(meta: &'a ReflectMeta<'b>) -> Self {
|
||||
Self {
|
||||
meta,
|
||||
active_fields: Box::new([]),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_fields(meta: &'a ReflectMeta<'b>, active_fields: Box<[Type]>) -> Self {
|
||||
Self {
|
||||
meta,
|
||||
active_fields,
|
||||
}
|
||||
}
|
||||
|
||||
/// Extends the `where` clause for a type with additional bounds needed for the reflection impls.
|
||||
///
|
||||
/// The default bounds added are as follows:
|
||||
/// - `Self` has the bounds `Any + Send + Sync`
|
||||
/// - Type parameters have the bound `TypePath` unless `#[reflect(type_path = false)]` is present
|
||||
/// - Active fields have the bounds `TypePath` and either `PartialReflect` if `#[reflect(from_reflect = false)]` is present
|
||||
/// or `FromReflect` otherwise (or no bounds at all if `#[reflect(no_field_bounds)]` is present)
|
||||
///
|
||||
/// When the derive is used with `#[reflect(where)]`, the bounds specified in the attribute are added as well.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```ignore (bevy_reflect is not accessible from this crate)
|
||||
/// #[derive(Reflect)]
|
||||
/// struct Foo<T, U> {
|
||||
/// a: T,
|
||||
/// #[reflect(ignore)]
|
||||
/// b: U
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Generates the following where clause:
|
||||
///
|
||||
/// ```ignore (bevy_reflect is not accessible from this crate)
|
||||
/// where
|
||||
/// // `Self` bounds:
|
||||
/// Self: Any + Send + Sync,
|
||||
/// // Type parameter bounds:
|
||||
/// T: TypePath,
|
||||
/// U: TypePath,
|
||||
/// // Field bounds
|
||||
/// T: FromReflect + TypePath,
|
||||
/// ```
|
||||
///
|
||||
/// If we had added `#[reflect(where T: MyTrait)]` to the type, it would instead generate:
|
||||
///
|
||||
/// ```ignore (bevy_reflect is not accessible from this crate)
|
||||
/// where
|
||||
/// // `Self` bounds:
|
||||
/// Self: Any + Send + Sync,
|
||||
/// // Type parameter bounds:
|
||||
/// T: TypePath,
|
||||
/// U: TypePath,
|
||||
/// // Field bounds
|
||||
/// T: FromReflect + TypePath,
|
||||
/// // Custom bounds
|
||||
/// T: MyTrait,
|
||||
/// ```
|
||||
///
|
||||
/// And if we also added `#[reflect(no_field_bounds)]` to the type, it would instead generate:
|
||||
///
|
||||
/// ```ignore (bevy_reflect is not accessible from this crate)
|
||||
/// where
|
||||
/// // `Self` bounds:
|
||||
/// Self: Any + Send + Sync,
|
||||
/// // Type parameter bounds:
|
||||
/// T: TypePath,
|
||||
/// U: TypePath,
|
||||
/// // Custom bounds
|
||||
/// T: MyTrait,
|
||||
/// ```
|
||||
pub fn extend_where_clause(&self, where_clause: Option<&WhereClause>) -> TokenStream {
|
||||
// We would normally just use `Self`, but that won't work for generating things like assertion functions
|
||||
// and trait impls for a type's reference (e.g. `impl FromArg for &MyType`)
|
||||
let this = self.meta.type_path().true_type();
|
||||
|
||||
let required_bounds = self.required_bounds();
|
||||
|
||||
// Maintain existing where clause, if any.
|
||||
let mut generic_where_clause = if let Some(where_clause) = where_clause {
|
||||
let predicates = where_clause.predicates.iter();
|
||||
quote! {where #this: #required_bounds, #(#predicates,)*}
|
||||
} else {
|
||||
quote!(where #this: #required_bounds,)
|
||||
};
|
||||
|
||||
// Add additional reflection trait bounds
|
||||
let predicates = self.predicates();
|
||||
generic_where_clause.extend(quote! {
|
||||
#predicates
|
||||
});
|
||||
|
||||
generic_where_clause
|
||||
}
|
||||
|
||||
/// Returns an iterator the where clause predicates to extended the where clause with.
|
||||
fn predicates(&self) -> Punctuated<TokenStream, Token![,]> {
|
||||
let mut predicates = Punctuated::new();
|
||||
|
||||
if let Some(type_param_predicates) = self.type_param_predicates() {
|
||||
predicates.extend(type_param_predicates);
|
||||
}
|
||||
|
||||
if let Some(field_predicates) = self.active_field_predicates() {
|
||||
predicates.extend(field_predicates);
|
||||
}
|
||||
|
||||
if let Some(custom_where) = self.meta.attrs().custom_where() {
|
||||
predicates.push(custom_where.predicates.to_token_stream());
|
||||
}
|
||||
|
||||
predicates
|
||||
}
|
||||
|
||||
/// Returns an iterator over the where clause predicates for the type parameters
|
||||
/// if they require one.
|
||||
fn type_param_predicates(&self) -> Option<impl Iterator<Item = TokenStream> + '_> {
|
||||
self.type_path_bound().map(|type_path_bound| {
|
||||
self.meta
|
||||
.type_path()
|
||||
.generics()
|
||||
.type_params()
|
||||
.map(move |param| {
|
||||
let ident = ¶m.ident;
|
||||
|
||||
quote!(#ident : #type_path_bound)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns an iterator over the where clause predicates for the active fields.
|
||||
fn active_field_predicates(&self) -> Option<impl Iterator<Item = TokenStream> + '_> {
|
||||
if self.meta.attrs().no_field_bounds() {
|
||||
None
|
||||
} else {
|
||||
let bevy_reflect_path = self.meta.bevy_reflect_path();
|
||||
let reflect_bound = self.reflect_bound();
|
||||
|
||||
// `TypePath` is always required for active fields since they are used to
|
||||
// construct `NamedField` and `UnnamedField` instances for the `Typed` impl.
|
||||
// Likewise, `GetTypeRegistration` is always required for active fields since
|
||||
// they are used to register the type's dependencies.
|
||||
Some(self.active_fields.iter().map(move |ty| {
|
||||
quote!(
|
||||
#ty : #reflect_bound
|
||||
+ #bevy_reflect_path::TypePath
|
||||
// Needed for `Typed` impls
|
||||
+ #bevy_reflect_path::MaybeTyped
|
||||
// Needed for `GetTypeRegistration` impls
|
||||
+ #bevy_reflect_path::__macro_exports::RegisterForReflection
|
||||
)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// The `PartialReflect` or `FromReflect` bound to use based on `#[reflect(from_reflect = false)]`.
|
||||
fn reflect_bound(&self) -> TokenStream {
|
||||
let bevy_reflect_path = self.meta.bevy_reflect_path();
|
||||
|
||||
if self.meta.from_reflect().should_auto_derive() {
|
||||
quote!(#bevy_reflect_path::FromReflect)
|
||||
} else {
|
||||
quote!(#bevy_reflect_path::PartialReflect)
|
||||
}
|
||||
}
|
||||
|
||||
/// The `TypePath` bounds to use based on `#[reflect(type_path = false)]`.
|
||||
fn type_path_bound(&self) -> Option<TokenStream> {
|
||||
if self.meta.type_path_attrs().should_auto_derive() {
|
||||
let bevy_reflect_path = self.meta.bevy_reflect_path();
|
||||
Some(quote!(#bevy_reflect_path::TypePath))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// The minimum required bounds for a type to be reflected.
|
||||
fn required_bounds(&self) -> TokenStream {
|
||||
quote!(#FQAny + #FQSend + #FQSync)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue