bevy/crates/bevy_reflect/derive/src/serialization.rs
Clar Fon 711246aa34
Update hashbrown to 0.15 (#15801)
Updating dependencies; adopted version of #15696. (Supercedes #15696.)

Long answer: hashbrown is no longer using ahash by default, meaning that
we can't use the default-hasher methods with ahasher. So, we have to use
the longer-winded versions instead. This takes the opportunity to also
switch our default hasher as well, but without actually enabling the
default-hasher feature for hashbrown, meaning that we'll be able to
change our hasher more easily at the cost of all of these method calls
being obnoxious forever.

One large change from 0.15 is that `insert_unique_unchecked` is now
`unsafe`, and for cases where unsafe code was denied at the crate level,
I replaced it with `insert`.

## Migration Guide

`bevy_utils` has updated its version of `hashbrown` to 0.15 and now
defaults to `foldhash` instead of `ahash`. This means that if you've
hard-coded your hasher to `bevy_utils::AHasher` or separately used the
`ahash` crate in your code, you may need to switch to `foldhash` to
ensure that everything works like it does in Bevy.
2024-12-10 19:45:50 +00:00

96 lines
3.2 KiB
Rust

use crate::{
derive_data::StructField,
field_attributes::{DefaultBehavior, ReflectIgnoreBehavior},
};
use bevy_macro_utils::fq_std::FQDefault;
use quote::quote;
use std::collections::HashMap;
use syn::{spanned::Spanned, Path};
type ReflectionIndex = usize;
/// Collected serialization data used to generate a `SerializationData` type.
pub(crate) struct SerializationDataDef {
/// Maps a field's _reflection_ index to its [`SkippedFieldDef`] if marked as `#[reflect(skip_serializing)]`.
skipped: HashMap<ReflectionIndex, SkippedFieldDef>,
}
impl SerializationDataDef {
/// Attempts to create a new `SerializationDataDef` from the given collection of fields.
///
/// Returns `Ok(Some(data))` if there are any fields needing to be skipped during serialization.
/// Otherwise, returns `Ok(None)`.
pub fn new(
fields: &[StructField<'_>],
bevy_reflect_path: &Path,
) -> Result<Option<Self>, syn::Error> {
let mut skipped = <HashMap<_, _>>::default();
for field in fields {
match field.attrs.ignore {
ReflectIgnoreBehavior::IgnoreSerialization => {
skipped.insert(
field.reflection_index.ok_or_else(|| {
syn::Error::new(
field.data.span(),
"internal error: field is missing a reflection index",
)
})?,
SkippedFieldDef::new(field, bevy_reflect_path)?,
);
}
_ => continue,
}
}
if skipped.is_empty() {
Ok(None)
} else {
Ok(Some(Self { skipped }))
}
}
/// Returns a `TokenStream` containing an initialized `SerializationData` type.
pub fn as_serialization_data(&self, bevy_reflect_path: &Path) -> proc_macro2::TokenStream {
let fields =
self.skipped
.iter()
.map(|(reflection_index, SkippedFieldDef { default_fn })| {
quote! {(
#reflection_index,
#bevy_reflect_path::serde::SkippedField::new(#default_fn)
)}
});
quote! {
#bevy_reflect_path::serde::SerializationData::new(
::core::iter::IntoIterator::into_iter([#(#fields),*])
)
}
}
}
/// Collected field data used to generate a `SkippedField` type.
pub(crate) struct SkippedFieldDef {
/// The default function for this field.
///
/// This is of type `fn() -> Box<dyn Reflect>`.
default_fn: proc_macro2::TokenStream,
}
impl SkippedFieldDef {
pub fn new(field: &StructField<'_>, bevy_reflect_path: &Path) -> Result<Self, syn::Error> {
let ty = &field.data.ty;
let default_fn = match &field.attrs.default {
DefaultBehavior::Func(func) => quote! {
|| { #bevy_reflect_path::__macro_exports::alloc_utils::Box::new(#func()) }
},
_ => quote! {
|| { #bevy_reflect_path::__macro_exports::alloc_utils::Box::new(<#ty as #FQDefault>::default()) }
},
};
Ok(Self { default_fn })
}
}