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 validate: Option<SpanWrapped<syn::Expr>>,
|
||||||
pub default: Option<syn::Expr>,
|
pub default: Option<syn::Expr>,
|
||||||
pub default_with: Option<syn::Expr>,
|
pub default_with: Option<syn::Expr>,
|
||||||
|
pub flatten: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FieldAttr {
|
impl FieldAttr {
|
||||||
|
@ -33,6 +34,7 @@ pub(crate) trait FieldExt {
|
||||||
fn first_field_name(&self) -> Result<Option<FieldName>>;
|
fn first_field_name(&self) -> Result<Option<FieldName>>;
|
||||||
fn stripped_ty(&self) -> syn::Type;
|
fn stripped_ty(&self) -> syn::Type;
|
||||||
fn name_buf_opt(&self) -> Result<TokenStream>;
|
fn name_buf_opt(&self) -> Result<TokenStream>;
|
||||||
|
fn is_flattened(&self) -> Result<bool>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(FromMeta)]
|
#[derive(FromMeta)]
|
||||||
|
@ -201,6 +203,13 @@ impl FieldExt for Field<'_> {
|
||||||
.map(|name| quote_spanned!(span => Some(#_form::NameBuf::from((__c.__parent, #name)))))
|
.map(|name| quote_spanned!(span => Some(#_form::NameBuf::from((__c.__parent, #name)))))
|
||||||
.unwrap_or_else(|| quote_spanned!(span => None::<#_form::NameBuf>)))
|
.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>);
|
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)
|
// F: fn(field_ty: Ty, field_context: Expr)
|
||||||
fn fields_map<F>(fields: Fields<'_>, map_f: F) -> Result<TokenStream>
|
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 matchers = vec![];
|
||||||
|
let mut flattened_field = None;
|
||||||
for field in fields.iter() {
|
for field in fields.iter() {
|
||||||
let (ident, ty) = (field.context_ident(), field.stripped_ty());
|
let (ident, ty) = (field.context_ident(), field.stripped_ty());
|
||||||
let field_context: syn::Expr = syn::parse2(quote_spanned!(ty.span() => {
|
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))
|
__c.#ident.get_or_insert_with(|| <#ty as #_form::FromForm<'r>>::init(__o))
|
||||||
})).expect("form context expression");
|
})).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 fields.are_unnamed() {
|
||||||
// If we have unnamed fields, then we have exactly one by virtue of
|
// If we have unnamed fields, then we have exactly one by virtue of
|
||||||
// the earlier validation. Push directly to it and return.
|
// 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() =>
|
return Ok(quote_spanned!(ident.span() =>
|
||||||
__c.__parent = __f.name.parent();
|
__c.__parent = __f.name.parent();
|
||||||
#push
|
#push
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
matchers.extend(field.field_names()?.into_iter().map(|f| match f {
|
let is_flattened = field.is_flattened()?;
|
||||||
Cased(name) => quote!(#name => { #push }),
|
|
||||||
Uncased(name) => quote!(__n if __n.as_uncased() == #name => { #push }),
|
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! {
|
Ok(quote! {
|
||||||
__c.__parent = __f.name.parent();
|
__c.__parent = __f.name.parent();
|
||||||
|
|
||||||
match __f.name.key_lossy().as_str() {
|
match __f.name.key_lossy().as_str() {
|
||||||
#(#matchers,)*
|
#(#matchers,)*
|
||||||
|
#(_ => { #flattened_field },)*
|
||||||
__k if __k == "_method" || !__c.__opts.strict => { /* ok */ },
|
__k if __k == "_method" || !__c.__opts.strict => { /* ok */ },
|
||||||
_ => __c.__errors.push(__f.unexpected()),
|
_ => __c.__errors.push(__f.unexpected()),
|
||||||
}
|
}
|
||||||
|
@ -181,8 +200,8 @@ pub fn derive_from_form(input: proc_macro::TokenStream) -> TokenStream {
|
||||||
#output
|
#output
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.try_fields_map(|_, f| fields_map(f, |ty, ctxt| quote_spanned!(ty.span() => {
|
.try_fields_map(|_, f| fields_map(f, |ty, ctxt, field| quote_spanned!(ty.span() => {
|
||||||
<#ty as #_form::FromForm<'r>>::push_value(#ctxt, __f.shift());
|
<#ty as #_form::FromForm<'r>>::push_value(#ctxt, #field);
|
||||||
})))
|
})))
|
||||||
)
|
)
|
||||||
.inner_mapper(MapperBuild::new()
|
.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
|
// 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.
|
// 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() => {
|
.try_fields_map(|_, f| fields_map(f, |ty, ctxt, field| quote_spanned!(ty.span() => {
|
||||||
let _fut = <#ty as #_form::FromForm<'r>>::push_data(#ctxt, __f.shift());
|
let _fut = <#ty as #_form::FromForm<'r>>::push_data(#ctxt, #field);
|
||||||
_fut.await;
|
_fut.await;
|
||||||
})))
|
})))
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue