rocket_codegen/derive/
from_form.rs

1use proc_macro2::TokenStream;
2use devise::ext::{TypeExt, SpanDiagnosticExt, GenericsExt, quote_respanned};
3use syn::parse::Parser;
4use devise::*;
5
6use crate::exports::*;
7use crate::derive::form_field::FieldName::*;
8use crate::derive::form_field::{FieldExt, default, first_duplicate, validators};
9use crate::syn_ext::{GenericsExt as _, TypeExt as _};
10
11type WherePredicates = syn::punctuated::Punctuated<syn::WherePredicate, syn::Token![,]>;
12
13macro_rules! quote_spanned {
14    ($span:expr => $($token:tt)*) => (
15        quote::quote_spanned!(
16            proc_macro2::Span::call_site().located_at($span) => $($token)*
17        )
18    )
19}
20
21// F: fn(field_ty: Ty, field_context: Expr)
22fn fields_map<F>(fields: Fields<'_>, map_f: F) -> Result<TokenStream>
23    where F: Fn(&syn::Type, &syn::Expr) -> TokenStream
24{
25    let mut matchers = vec![];
26    for field in fields.iter() {
27        let (ident, ty) = (field.context_ident(), field.stripped_ty());
28        let field_context: syn::Expr = syn::parse2(quote_spanned!(ty.span() => {
29            let __o = __c.__opts;
30            __c.#ident.get_or_insert_with(|| <#ty as #_form::FromForm<'r>>::init(__o))
31        })).expect("form context expression");
32
33        let push = map_f(&ty, &field_context);
34        if fields.are_unnamed() {
35            // If we have unnamed fields, then we have exactly one by virtue of
36            // the earlier validation. Push directly to it and return.
37            return Ok(quote_spanned!(ident.span() =>
38                __c.__parent = __f.name.parent();
39                 #push
40            ));
41        }
42
43        matchers.extend(field.field_names()?.into_iter().map(|f| match f {
44            Cased(f) => quote_spanned!(ty.span() => #f => { #push }),
45            Uncased(f) => quote_spanned!(ty.span() => __n if __n.as_uncased() == #f => { #push }),
46        }));
47    }
48
49    Ok(quote! {
50        __c.__parent = __f.name.parent();
51
52        match __f.name.key_lossy().as_str() {
53            #(#matchers,)*
54            __k if __k == "_method" || !__c.__opts.strict => { /* ok */ },
55            _ => __c.__errors.push(__f.unexpected()),
56        }
57    })
58}
59
60fn generic_bounds_tokens(input: Input<'_>) -> Result<TokenStream> {
61    MapperBuild::new()
62        .try_enum_map(|m, e| mapper::enum_null(m, e))
63        .try_fields_map(|_, fields| {
64            let generic_idents = fields.parent.input().generics().type_idents();
65
66            let bounds = fields.iter()
67                .filter(|f| !f.ty.is_concrete(&generic_idents))
68                .map(|f| f.ty.with_replaced_lifetimes(syn::Lifetime::new("'r", f.ty.span())))
69                .map(|ty| quote_spanned!(ty.span() => #ty: #_form::FromForm<'r>));
70
71            Ok(quote!(#(#bounds),*))
72        })
73        .map_input(input)
74}
75
76fn generic_bounds(input: Input<'_>) -> Result<WherePredicates> {
77    Ok(WherePredicates::parse_terminated.parse2(generic_bounds_tokens(input)?)?)
78}
79
80fn context_type(input: Input<'_>) -> Result<(TokenStream, syn::Generics)> {
81    let mut gen = input.generics().clone();
82
83    let lifetime = syn::parse_quote!('r);
84    if !gen.replace_lifetime(0, &lifetime) {
85        gen.insert_lifetime(syn::LifetimeParam::new(lifetime.clone()));
86    }
87
88    gen.add_where_predicates(generic_bounds(input)?);
89    let ty = quote_spanned!(input.ident().span() => FromFormGeneratedContext);
90    Ok((ty, gen))
91}
92
93pub fn derive_from_form(input: proc_macro::TokenStream) -> TokenStream {
94    DeriveGenerator::build_for(input, quote!(impl<'r> #_form::FromForm<'r>))
95        .support(Support::Struct | Support::Lifetime | Support::Type)
96        .replace_generic(0, 0)
97        .type_bound_mapper(MapperBuild::new().try_input_map(|_, i| generic_bounds_tokens(i)))
98        .validator(ValidatorBuild::new()
99            .input_validate(|_, i| match i.generics().lifetimes().enumerate().last() {
100                Some((i, lt)) if i >= 1 => Err(lt.span().error("only one lifetime is supported")),
101                _ => Ok(())
102            })
103            .fields_validate(|_, fields| {
104                if fields.is_empty() {
105                    return Err(fields.span().error("at least one field is required"));
106                } else if fields.are_unnamed() && fields.count() != 1 {
107                    return Err(fields.span().error("tuple struct must have exactly one field"));
108                } else if let Some(d) = first_duplicate(fields.iter(), |f| f.field_names())? {
109                    let (field_a_i, field_a, name_a) = d.0;
110                    let (field_b_i, field_b, name_b) = d.1;
111
112                    if field_a_i == field_b_i {
113                        return Err(field_a.error("field has conflicting names")
114                            .span_note(name_a, "this field name...")
115                            .span_note(name_b, "...conflicts with this field name"));
116                    }
117
118                    return Err(name_b.error("field name conflicts with previous name")
119                        .span_help(field_b, "declared in this field")
120                        .span_note(field_a, "previous field with conflicting name"));
121                }
122
123                Ok(())
124            })
125        )
126        .outer_mapper(MapperBuild::new()
127            .try_input_map(|mapper, input|  {
128                let vis = input.vis();
129                let (ctxt_ty, gen) = context_type(input)?;
130                let (impl_gen, _, where_clause)  = gen.split_for_impl();
131                let output = mapper::input_default(mapper, input)?;
132                Ok(quote_spanned! { mixed(input.span()) =>
133                    /// Rocket generated FormForm context.
134                    #[doc(hidden)]
135                    #[allow(unknown_lints)]
136                    #[allow(renamed_and_removed_lints)]
137                    #[allow(private_in_public)]
138                    #[allow(private_bounds)]
139                    #vis struct #ctxt_ty #impl_gen #where_clause {
140                        __opts: #_form::Options,
141                        __errors: #_form::Errors<'r>,
142                        __parent: #_Option<&'r #_form::Name>,
143                        #output
144                    }
145                })
146            })
147            .try_fields_map(|m, f| mapper::fields_null(m, f))
148            .field_map(|_, field| {
149                let ident = field.context_ident();
150                let mut ty = field.stripped_ty();
151                ty.replace_lifetimes(syn::parse_quote!('r));
152                let field_ty = quote_respanned!(ty.span() =>
153                    #_Option<<#ty as #_form::FromForm<'r>>::Context>
154                );
155
156                quote_spanned!(ty.span() => #ident: #field_ty,)
157            })
158        )
159        .outer_mapper(quote! {
160            #[allow(unused_imports)]
161            use #_http::uncased::AsUncased;
162        })
163        .outer_mapper(quote!(#[allow(clippy::all, clippy::pedantic, clippy::nursery)]))
164        .outer_mapper(quote!(#[allow(renamed_and_removed_lints)]))
165        .outer_mapper(quote!(#[allow(private_in_public)]))
166        .outer_mapper(quote!(#[rocket::async_trait]))
167        .inner_mapper(MapperBuild::new()
168            .try_input_map(|mapper, input| {
169                let (ctxt_ty, gen) = context_type(input)?;
170                let (_, ty_gen, _) = gen.split_for_impl();
171                let output = mapper::input_default(mapper, input)?;
172                Ok(quote! {
173                    type Context = #ctxt_ty #ty_gen;
174
175                    fn init(__opts: #_form::Options) -> Self::Context {
176                        Self::Context {
177                            __opts,
178                            __errors: #_form::Errors::new(),
179                            __parent: #_None,
180                            #output
181                        }
182                    }
183                })
184            })
185            .try_fields_map(|m, f| mapper::fields_null(m, f))
186            .field_map(|_, field| {
187                let ident = field.context_ident();
188                let ty = field.ty.with_stripped_lifetimes();
189                quote_spanned!(ty.span() => #ident: #_None,)
190            })
191        )
192        .inner_mapper(MapperBuild::new()
193            .with_output(|_, output| quote! {
194                fn push_value(__c: &mut Self::Context, __f: #_form::ValueField<'r>) {
195                    #output
196                }
197            })
198            .try_fields_map(|_, f| fields_map(f, |ty, ctxt| quote_spanned!(ty.span() => {
199                <#ty as #_form::FromForm<'r>>::push_value(#ctxt, __f.shift());
200            })))
201        )
202        .inner_mapper(MapperBuild::new()
203            .try_input_map(|mapper, input| {
204                let (ctxt_ty, gen) = context_type(input)?;
205                let (_, ty_gen, _) = gen.split_for_impl();
206                let output = mapper::input_default(mapper, input)?;
207                Ok(quote_spanned! { ctxt_ty.span() =>
208                    async fn push_data(
209                        __c: &mut #ctxt_ty #ty_gen,
210                        __f: #_form::DataField<'r, '_>
211                    ) {
212                        #output
213                    }
214                })
215            })
216            // Without the `let _fut`, we get a wild lifetime error. It don't
217            // make no sense, Rust async/await: it don't make no sense.
218            .try_fields_map(|_, f| fields_map(f, |ty, ctxt| quote_spanned!(ty.span() => {
219                let __fut = <#ty as #_form::FromForm<'r>>::push_data(#ctxt, __f.shift());
220                __fut.await;
221            })))
222        )
223        .inner_mapper(MapperBuild::new()
224            .with_output(|_, _| quote! {
225                fn push_error(__c: &mut Self::Context, __e: #_form::Error<'r>) {
226                    __c.__errors.push(__e);
227                }
228            }))
229        .inner_mapper(MapperBuild::new()
230            .with_output(|_, output| quote! {
231                fn finalize(mut __c: Self::Context) -> #_Result<Self, #_form::Errors<'r>> {
232                    #[allow(unused_imports)]
233                    use #_form::validate::*;
234
235                    #output
236                }
237            })
238            .try_fields_map(|mapper, fields| {
239                // This validates the attributes so we can `unwrap()` later.
240                let finalize_field = fields.iter()
241                    .map(|f| mapper.map_field(f))
242                    .collect::<Result<Vec<TokenStream>>>()?;
243
244                let (_ok, _some, _err, _none) = (_Ok, _Some, _Err, _None);
245                let validator = fields.iter().flat_map(|f| validators(f).unwrap());
246                let ident = fields.iter().map(|f| f.context_ident());
247                let builder = fields.builder(|f| {
248                    let ident = f.context_ident();
249                    quote_spanned!(ident.span() => #ident.unwrap())
250                });
251
252                Ok(quote_spanned!(fields.span() =>
253                    #(
254                        let #ident = match #finalize_field {
255                            #_ok(#ident) => #_some(#ident),
256                            #_err(__e) => { __c.__errors.extend(__e); #_none }
257                        };
258                    )*
259
260                    #(
261                        if let #_err(__e) = #validator {
262                            __c.__errors.extend(__e);
263                        }
264                    )*
265
266                    if !__c.__errors.is_empty() {
267                        return #_Err(__c.__errors);
268                    }
269
270                    Ok(#builder)
271                ))
272            })
273            .try_field_map(|_, f| {
274                let (ident, ty) = (f.context_ident(), f.stripped_ty());
275                let name_buf_opt = f.name_buf_opt()?;
276                let default = default(f)?
277                    .unwrap_or_else(|| quote_spanned!(ty.span() => {
278                        <#ty as #_form::FromForm<'r>>::default(__opts)
279                    }));
280
281                Ok(quote_spanned! { ty.span() => {
282                    let __opts = __c.__opts;
283                    let __name = #name_buf_opt;
284                    __c.#ident
285                        .map_or_else(
286                            || #default.ok_or_else(|| #_form::ErrorKind::Missing.into()),
287                            <#ty as #_form::FromForm<'r>>::finalize
288                        )
289                        .map_err(|__e| match __name {
290                            #_Some(__name) => __e.with_name(__name),
291                            #_None => __e,
292                        })
293                        .map_err(|__e| __e.is_empty()
294                            .then(|| #_form::ErrorKind::Unknown.into())
295                            .unwrap_or(__e))
296                }})
297            })
298        )
299        .to_tokens()
300}