Skip to content

Commit e4a5bda

Browse files
committed
Added #[reflect(default)] attribute
1 parent dbd856d commit e4a5bda

File tree

2 files changed

+70
-9
lines changed

2 files changed

+70
-9
lines changed

crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,37 @@
77
use crate::REFLECT_ATTRIBUTE_NAME;
88
use quote::ToTokens;
99
use syn::spanned::Spanned;
10-
use syn::{Attribute, Meta, NestedMeta};
10+
use syn::{Attribute, Lit, Meta, NestedMeta};
1111

1212
pub(crate) static IGNORE_ATTR: &str = "ignore";
13+
pub(crate) static DEFAULT_ATTR: &str = "default";
1314

1415
/// A container for attributes defined on a field reflected type's field.
1516
#[derive(Default)]
1617
pub(crate) struct ReflectFieldAttr {
1718
/// Determines if this field should be ignored.
1819
pub ignore: bool,
20+
/// Sets the default behavior of this field.
21+
pub default: DefaultBehavior,
22+
}
23+
24+
/// Controls how the default value is determined for a field.
25+
pub(crate) enum DefaultBehavior {
26+
/// Field is required.
27+
Required,
28+
/// Field can be defaulted using `Default::default()`.
29+
Default,
30+
/// Field can be created using the given function name.
31+
///
32+
/// This assumes the function is in scope, is callable with zero arguments,
33+
/// and returns the expected type.
34+
Func(syn::ExprPath),
35+
}
36+
37+
impl Default for DefaultBehavior {
38+
fn default() -> Self {
39+
Self::Required
40+
}
1941
}
2042

2143
/// Parse all field attributes marked "reflect" (such as `#[reflect(ignore)]`).
@@ -50,10 +72,29 @@ fn parse_meta(args: &mut ReflectFieldAttr, meta: &Meta) -> Result<(), syn::Error
5072
args.ignore = true;
5173
Ok(())
5274
}
75+
Meta::Path(path) if path.is_ident(DEFAULT_ATTR) => {
76+
args.default = DefaultBehavior::Default;
77+
Ok(())
78+
}
5379
Meta::Path(path) => Err(syn::Error::new(
5480
path.span(),
5581
format!("unknown attribute parameter: {}", path.to_token_stream()),
5682
)),
83+
Meta::NameValue(pair) if pair.path.is_ident(DEFAULT_ATTR) => {
84+
let lit = &pair.lit;
85+
match lit {
86+
Lit::Str(lit_str) => {
87+
args.default = DefaultBehavior::Func(lit_str.parse()?);
88+
Ok(())
89+
}
90+
err => {
91+
Err(syn::Error::new(
92+
err.span(),
93+
format!("expected a string literal containing the name of a function, but found: {}", err.to_token_stream()),
94+
))
95+
}
96+
}
97+
}
5798
Meta::NameValue(pair) => {
5899
let path = &pair.path;
59100
Err(syn::Error::new(

crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::field_attributes::DefaultBehavior;
12
use crate::ReflectDeriveData;
23
use proc_macro::TokenStream;
34
use proc_macro2::Span;
@@ -112,8 +113,10 @@ fn get_ignored_fields(derive_data: &ReflectDeriveData, is_tuple: bool) -> Member
112113
.ignored_fields()
113114
.map(|field| {
114115
let member = get_ident(field.data, field.index, is_tuple);
115-
let value = quote! {
116-
Default::default()
116+
117+
let value = match &field.attrs.default {
118+
DefaultBehavior::Func(path) => quote! {#path()},
119+
_ => quote! {Default::default()},
117120
};
118121

119122
(member, value)
@@ -139,12 +142,29 @@ fn get_active_fields(
139142
let accessor = get_field_accessor(field.data, field.index, is_tuple);
140143
let ty = field.data.ty.clone();
141144

142-
let value = quote! { {
143-
<#ty as #bevy_reflect_path::FromReflect>::from_reflect(
144-
// Accesses the field on the given dynamic struct or tuple struct
145-
#bevy_reflect_path::#struct_type::field(#dyn_struct_name, #accessor)?
146-
)?
147-
}};
145+
let get_field = quote! {
146+
#bevy_reflect_path::#struct_type::field(#dyn_struct_name, #accessor)
147+
};
148+
149+
let value = match &field.attrs.default {
150+
DefaultBehavior::Func(path) => quote! {
151+
if let Some(field) = #get_field {
152+
<#ty as #bevy_reflect_path::FromReflect>::from_reflect(field)?
153+
} else {
154+
#path()
155+
}
156+
},
157+
DefaultBehavior::Default => quote! {
158+
if let Some(field) = #get_field {
159+
<#ty as #bevy_reflect_path::FromReflect>::from_reflect(field)?
160+
} else {
161+
Default::default()
162+
}
163+
},
164+
DefaultBehavior::Required => quote! {
165+
<#ty as #bevy_reflect_path::FromReflect>::from_reflect(#get_field?)?
166+
},
167+
};
148168

149169
(member, value)
150170
})

0 commit comments

Comments
 (0)