 efda7f3f9c
			
		
	
	
		efda7f3f9c
		
			
		
	
	
	
	
		
			
			Takes the first two commits from #15375 and adds suggestions from this comment: https://github.com/bevyengine/bevy/pull/15375#issuecomment-2366968300 See #15375 for more reasoning/motivation. ## Rebasing (rerunning) ```rust git switch simpler-lint-fixes git reset --hard main cargo fmt --all -- --unstable-features --config normalize_comments=true,imports_granularity=Crate cargo fmt --all git add --update git commit --message "rustfmt" cargo clippy --workspace --all-targets --all-features --fix cargo fmt --all -- --unstable-features --config normalize_comments=true,imports_granularity=Crate cargo fmt --all git add --update git commit --message "clippy" git cherry-pick e6c0b94f6795222310fb812fa5c4512661fc7887 ```
		
			
				
	
	
		
			237 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			237 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| //! Contains code related to field attributes for reflected types.
 | |
| //!
 | |
| //! A field attribute is an attribute which applies to particular field or variant
 | |
| //! 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, custom_attributes::CustomAttributes,
 | |
|     REFLECT_ATTRIBUTE_NAME,
 | |
| };
 | |
| use quote::ToTokens;
 | |
| use syn::{parse::ParseStream, Attribute, LitStr, Meta, Token, Type};
 | |
| 
 | |
| mod kw {
 | |
|     syn::custom_keyword!(ignore);
 | |
|     syn::custom_keyword!(skip_serializing);
 | |
|     syn::custom_keyword!(default);
 | |
|     syn::custom_keyword!(remote);
 | |
| }
 | |
| 
 | |
| pub(crate) const IGNORE_SERIALIZATION_ATTR: &str = "skip_serializing";
 | |
| pub(crate) const IGNORE_ALL_ATTR: &str = "ignore";
 | |
| 
 | |
| pub(crate) const DEFAULT_ATTR: &str = "default";
 | |
| 
 | |
| /// Stores data about if the field should be visible via the Reflect and serialization interfaces
 | |
| ///
 | |
| /// Note the relationship between serialization and reflection is such that a member must be reflected in order to be serialized.
 | |
| /// In boolean logic this is described as: `is_serialized -> is_reflected`, this means we can reflect something without serializing it but not the other way round.
 | |
| /// The `is_reflected` predicate is provided as `self.is_active()`
 | |
| #[derive(Default, Clone, Copy, PartialEq, Eq)]
 | |
| pub(crate) enum ReflectIgnoreBehavior {
 | |
|     /// Don't ignore, appear to all systems
 | |
|     #[default]
 | |
|     None,
 | |
|     /// Ignore when serializing but not when reflecting
 | |
|     IgnoreSerialization,
 | |
|     /// Ignore both when serializing and reflecting
 | |
|     IgnoreAlways,
 | |
| }
 | |
| 
 | |
| impl ReflectIgnoreBehavior {
 | |
|     /// Returns `true` if the ignoring behavior implies member is included in the reflection API, and false otherwise.
 | |
|     pub fn is_active(self) -> bool {
 | |
|         match self {
 | |
|             ReflectIgnoreBehavior::None | ReflectIgnoreBehavior::IgnoreSerialization => true,
 | |
|             ReflectIgnoreBehavior::IgnoreAlways => false,
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /// The exact logical opposite of `self.is_active()` returns true iff this member is not part of the reflection API whatsoever (neither serialized nor reflected)
 | |
|     pub fn is_ignored(self) -> bool {
 | |
|         !self.is_active()
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Controls how the default value is determined for a field.
 | |
| #[derive(Default, Clone)]
 | |
| pub(crate) enum DefaultBehavior {
 | |
|     /// Field is required.
 | |
|     #[default]
 | |
|     Required,
 | |
|     /// Field can be defaulted using `Default::default()`.
 | |
|     Default,
 | |
|     /// Field can be created using the given function name.
 | |
|     ///
 | |
|     /// This assumes the function is in scope, is callable with zero arguments,
 | |
|     /// and returns the expected type.
 | |
|     Func(syn::ExprPath),
 | |
| }
 | |
| 
 | |
| /// A container for attributes defined on a reflected type's field.
 | |
| #[derive(Default, Clone)]
 | |
| pub(crate) struct FieldAttributes {
 | |
|     /// Determines how this field should be ignored if at all.
 | |
|     pub ignore: ReflectIgnoreBehavior,
 | |
|     /// Sets the default behavior of this field.
 | |
|     pub default: DefaultBehavior,
 | |
|     /// Custom attributes created via `#[reflect(@...)]`.
 | |
|     pub custom_attributes: CustomAttributes,
 | |
|     /// For defining the remote wrapper type that should be used in place of the field for reflection logic.
 | |
|     pub remote: Option<Type>,
 | |
| }
 | |
| 
 | |
| impl FieldAttributes {
 | |
|     /// Parse all field attributes marked "reflect" (such as `#[reflect(ignore)]`).
 | |
|     pub fn parse_attributes(attrs: &[Attribute]) -> syn::Result<Self> {
 | |
|         let mut args = FieldAttributes::default();
 | |
| 
 | |
|         attrs
 | |
|             .iter()
 | |
|             .filter_map(|attr| {
 | |
|                 if !attr.path().is_ident(REFLECT_ATTRIBUTE_NAME) {
 | |
|                     // Not a reflect attribute -> skip
 | |
|                     return None;
 | |
|                 }
 | |
| 
 | |
|                 let Meta::List(meta) = &attr.meta else {
 | |
|                     return Some(syn::Error::new_spanned(attr, "expected meta list"));
 | |
|                 };
 | |
| 
 | |
|                 // Parse all attributes inside the list, collecting any errors
 | |
|                 meta.parse_args_with(terminated_parser(Token![,], |stream| {
 | |
|                     args.parse_field_attribute(stream)
 | |
|                 }))
 | |
|                 .err()
 | |
|             })
 | |
|             .reduce(|mut acc, err| {
 | |
|                 acc.combine(err);
 | |
|                 acc
 | |
|             })
 | |
|             .map_or(Ok(args), Err)
 | |
|     }
 | |
| 
 | |
|     /// Parses a single field attribute.
 | |
|     fn parse_field_attribute(&mut self, input: ParseStream) -> syn::Result<()> {
 | |
|         let lookahead = input.lookahead1();
 | |
|         if lookahead.peek(Token![@]) {
 | |
|             self.parse_custom_attribute(input)
 | |
|         } else if lookahead.peek(kw::ignore) {
 | |
|             self.parse_ignore(input)
 | |
|         } else if lookahead.peek(kw::skip_serializing) {
 | |
|             self.parse_skip_serializing(input)
 | |
|         } else if lookahead.peek(kw::default) {
 | |
|             self.parse_default(input)
 | |
|         } else if lookahead.peek(kw::remote) {
 | |
|             self.parse_remote(input)
 | |
|         } else {
 | |
|             Err(lookahead.error())
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /// Parse `ignore` attribute.
 | |
|     ///
 | |
|     /// Examples:
 | |
|     /// - `#[reflect(ignore)]`
 | |
|     fn parse_ignore(&mut self, input: ParseStream) -> syn::Result<()> {
 | |
|         if self.ignore != ReflectIgnoreBehavior::None {
 | |
|             return Err(input.error(format!(
 | |
|                 "only one of {:?} is allowed",
 | |
|                 [IGNORE_ALL_ATTR, IGNORE_SERIALIZATION_ATTR]
 | |
|             )));
 | |
|         }
 | |
| 
 | |
|         input.parse::<kw::ignore>()?;
 | |
|         self.ignore = ReflectIgnoreBehavior::IgnoreAlways;
 | |
|         Ok(())
 | |
|     }
 | |
| 
 | |
|     /// Parse `skip_serializing` attribute.
 | |
|     ///
 | |
|     /// Examples:
 | |
|     /// - `#[reflect(skip_serializing)]`
 | |
|     fn parse_skip_serializing(&mut self, input: ParseStream) -> syn::Result<()> {
 | |
|         if self.ignore != ReflectIgnoreBehavior::None {
 | |
|             return Err(input.error(format!(
 | |
|                 "only one of {:?} is allowed",
 | |
|                 [IGNORE_ALL_ATTR, IGNORE_SERIALIZATION_ATTR]
 | |
|             )));
 | |
|         }
 | |
| 
 | |
|         input.parse::<kw::skip_serializing>()?;
 | |
|         self.ignore = ReflectIgnoreBehavior::IgnoreSerialization;
 | |
|         Ok(())
 | |
|     }
 | |
| 
 | |
|     /// Parse `default` attribute.
 | |
|     ///
 | |
|     /// Examples:
 | |
|     /// - `#[reflect(default)]`
 | |
|     /// - `#[reflect(default = "path::to::func")]`
 | |
|     fn parse_default(&mut self, input: ParseStream) -> syn::Result<()> {
 | |
|         if !matches!(self.default, DefaultBehavior::Required) {
 | |
|             return Err(input.error(format!("only one of {:?} is allowed", [DEFAULT_ATTR])));
 | |
|         }
 | |
| 
 | |
|         input.parse::<kw::default>()?;
 | |
| 
 | |
|         if input.peek(Token![=]) {
 | |
|             input.parse::<Token![=]>()?;
 | |
| 
 | |
|             let lit = input.parse::<LitStr>()?;
 | |
|             self.default = DefaultBehavior::Func(lit.parse()?);
 | |
|         } else {
 | |
|             self.default = DefaultBehavior::Default;
 | |
|         }
 | |
| 
 | |
|         Ok(())
 | |
|     }
 | |
| 
 | |
|     /// Parse `@` (custom attribute) attribute.
 | |
|     ///
 | |
|     /// Examples:
 | |
|     /// - `#[reflect(@(foo = "bar"))]`
 | |
|     /// - `#[reflect(@(min = 0.0, max = 1.0))]`
 | |
|     fn parse_custom_attribute(&mut self, input: ParseStream) -> syn::Result<()> {
 | |
|         self.custom_attributes.parse_custom_attribute(input)
 | |
|     }
 | |
| 
 | |
|     /// Parse `remote` attribute.
 | |
|     ///
 | |
|     /// Examples:
 | |
|     /// - `#[reflect(remote = path::to::RemoteType)]`
 | |
|     fn parse_remote(&mut self, input: ParseStream) -> syn::Result<()> {
 | |
|         if let Some(remote) = self.remote.as_ref() {
 | |
|             return Err(input.error(format!(
 | |
|                 "remote type already specified as {}",
 | |
|                 remote.to_token_stream()
 | |
|             )));
 | |
|         }
 | |
| 
 | |
|         input.parse::<kw::remote>()?;
 | |
|         input.parse::<Token![=]>()?;
 | |
| 
 | |
|         self.remote = Some(input.parse()?);
 | |
| 
 | |
|         Ok(())
 | |
|     }
 | |
| 
 | |
|     /// Returns `Some(true)` if the field has a generic remote type.
 | |
|     ///
 | |
|     /// If the remote type is not generic, returns `Some(false)`.
 | |
|     ///
 | |
|     /// If the field does not have a remote type, returns `None`.
 | |
|     pub fn is_remote_generic(&self) -> Option<bool> {
 | |
|         if let Type::Path(type_path) = self.remote.as_ref()? {
 | |
|             type_path
 | |
|                 .path
 | |
|                 .segments
 | |
|                 .last()
 | |
|                 .map(|segment| !segment.arguments.is_empty())
 | |
|         } else {
 | |
|             Some(false)
 | |
|         }
 | |
|     }
 | |
| }
 |