mirror of https://github.com/rwf2/Rocket.git
Added implementation of #[field(flatten)].
Limited to on flattend struct
This commit is contained in:
parent
2fc4b156eb
commit
c73bc939c1
|
@ -19,6 +19,7 @@ pub struct FieldAttr {
|
|||
pub validate: Option<SpanWrapped<syn::Expr>>,
|
||||
pub default: Option<syn::Expr>,
|
||||
pub default_with: Option<syn::Expr>,
|
||||
pub flatten: Option<bool>,
|
||||
}
|
||||
|
||||
impl FieldAttr {
|
||||
|
@ -33,6 +34,7 @@ pub(crate) trait FieldExt {
|
|||
fn first_field_name(&self) -> Result<Option<FieldName>>;
|
||||
fn stripped_ty(&self) -> syn::Type;
|
||||
fn name_buf_opt(&self) -> Result<TokenStream>;
|
||||
fn is_flattened(&self) -> Result<bool>;
|
||||
}
|
||||
|
||||
#[derive(FromMeta)]
|
||||
|
@ -201,6 +203,13 @@ impl FieldExt for Field<'_> {
|
|||
.map(|name| quote_spanned!(span => Some(#_form::NameBuf::from((__c.__parent, #name)))))
|
||||
.unwrap_or_else(|| quote_spanned!(span => None::<#_form::NameBuf>)))
|
||||
}
|
||||
|
||||
fn is_flattened(&self) -> Result<bool> {
|
||||
Ok(FieldAttr::from_attrs(FieldAttr::NAME, &self.attrs)?
|
||||
.into_iter()
|
||||
.filter_map(|attr| attr.flatten)
|
||||
.last().unwrap_or(false))
|
||||
}
|
||||
}
|
||||
|
||||
struct RecordMemberAccesses(Vec<syn::Member>);
|
||||
|
|
|
@ -11,9 +11,10 @@ type WherePredicates = syn::punctuated::Punctuated<syn::WherePredicate, syn::Tok
|
|||
|
||||
// F: fn(field_ty: Ty, field_context: Expr)
|
||||
fn fields_map<F>(fields: Fields<'_>, map_f: F) -> Result<TokenStream>
|
||||
where F: Fn(&syn::Type, &syn::Expr) -> TokenStream
|
||||
where F: Fn(&syn::Type, &syn::Expr, &syn::Expr) -> TokenStream
|
||||
{
|
||||
let mut matchers = vec![];
|
||||
let mut flattened_field = None;
|
||||
for field in fields.iter() {
|
||||
let (ident, ty) = (field.context_ident(), field.stripped_ty());
|
||||
let field_context: syn::Expr = syn::parse2(quote_spanned!(ty.span() => {
|
||||
|
@ -21,27 +22,45 @@ fn fields_map<F>(fields: Fields<'_>, map_f: F) -> Result<TokenStream>
|
|||
__c.#ident.get_or_insert_with(|| <#ty as #_form::FromForm<'r>>::init(__o))
|
||||
})).expect("form context expression");
|
||||
|
||||
let push = map_f(&ty, &field_context);
|
||||
let push = |field: &syn::Expr| map_f(&ty, &field_context, field);
|
||||
if fields.are_unnamed() {
|
||||
// If we have unnamed fields, then we have exactly one by virtue of
|
||||
// the earlier validation. Push directly to it and return.
|
||||
let push = push(&syn::parse2(quote!(__f.shift())).expect("field value expression"));
|
||||
return Ok(quote_spanned!(ident.span() =>
|
||||
__c.__parent = __f.name.parent();
|
||||
#push
|
||||
));
|
||||
}
|
||||
|
||||
matchers.extend(field.field_names()?.into_iter().map(|f| match f {
|
||||
Cased(name) => quote!(#name => { #push }),
|
||||
Uncased(name) => quote!(__n if __n.as_uncased() == #name => { #push }),
|
||||
}));
|
||||
let is_flattened = field.is_flattened()?;
|
||||
|
||||
if is_flattened {
|
||||
let push = push(&syn::parse2(quote!(__f)).expect("field value expression"));
|
||||
if let Some(flattened_field) = flattened_field {
|
||||
drop(flattened_field);
|
||||
todo!("Emit an error.");
|
||||
} else {
|
||||
flattened_field = Some(push);
|
||||
}
|
||||
} else {
|
||||
let push = push(&syn::parse2(quote!(__f.shift())).expect("field value expression"));
|
||||
matchers.extend(field.field_names()?.into_iter().map(|f| match f {
|
||||
Cased(name) => quote!(#name => { #push }),
|
||||
Uncased(name) => quote!(__n if __n.as_uncased() == #name => { #push }),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: check in options that they aren't strict or pass to flattened fields strict = false.
|
||||
|
||||
let flattened_field = flattened_field.iter();
|
||||
Ok(quote! {
|
||||
__c.__parent = __f.name.parent();
|
||||
|
||||
match __f.name.key_lossy().as_str() {
|
||||
#(#matchers,)*
|
||||
#(_ => { #flattened_field },)*
|
||||
__k if __k == "_method" || !__c.__opts.strict => { /* ok */ },
|
||||
_ => __c.__errors.push(__f.unexpected()),
|
||||
}
|
||||
|
@ -181,8 +200,8 @@ pub fn derive_from_form(input: proc_macro::TokenStream) -> TokenStream {
|
|||
#output
|
||||
}
|
||||
})
|
||||
.try_fields_map(|_, f| fields_map(f, |ty, ctxt| quote_spanned!(ty.span() => {
|
||||
<#ty as #_form::FromForm<'r>>::push_value(#ctxt, __f.shift());
|
||||
.try_fields_map(|_, f| fields_map(f, |ty, ctxt, field| quote_spanned!(ty.span() => {
|
||||
<#ty as #_form::FromForm<'r>>::push_value(#ctxt, #field);
|
||||
})))
|
||||
)
|
||||
.inner_mapper(MapperBuild::new()
|
||||
|
@ -201,8 +220,8 @@ pub fn derive_from_form(input: proc_macro::TokenStream) -> TokenStream {
|
|||
})
|
||||
// Without the `let _fut`, we get a wild lifetime error. It don't
|
||||
// make no sense, Rust async/await: it don't make no sense.
|
||||
.try_fields_map(|_, f| fields_map(f, |ty, ctxt| quote_spanned!(ty.span() => {
|
||||
let _fut = <#ty as #_form::FromForm<'r>>::push_data(#ctxt, __f.shift());
|
||||
.try_fields_map(|_, f| fields_map(f, |ty, ctxt, field| quote_spanned!(ty.span() => {
|
||||
let _fut = <#ty as #_form::FromForm<'r>>::push_data(#ctxt, #field);
|
||||
_fut.await;
|
||||
})))
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue