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
21fn 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 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 => { },
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 #[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 .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 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}