rocket_codegen/derive/
from_form_field.rs

1use devise::{*, ext::SpanDiagnosticExt};
2use proc_macro2::TokenStream;
3
4use crate::exports::*;
5use crate::derive::form_field::{VariantExt, first_duplicate};
6
7pub fn derive_from_form_field(input: proc_macro::TokenStream) -> TokenStream {
8    DeriveGenerator::build_for(input, quote!(impl<'__v> #_form::FromFormField<'__v>))
9        .support(Support::Enum)
10        .validator(ValidatorBuild::new()
11            // We only accept C-like enums with at least one variant.
12            .fields_validate(|_, fields| {
13                if !fields.is_empty() {
14                    return Err(fields.span().error("variants cannot have fields"));
15                }
16
17                Ok(())
18            })
19            .enum_validate(|_, data| {
20                if data.variants.is_empty() {
21                    return Err(data.span().error("enum must have at least one variant"));
22                }
23
24                if let Some(d) = first_duplicate(data.variants(), |v| v.form_field_values())? {
25                    let (variant_a_i, variant_a, value_a) = d.0;
26                    let (variant_b_i, variant_b, value_b) = d.1;
27
28                    if variant_a_i == variant_b_i {
29                        return Err(variant_a.error("variant has conflicting values")
30                            .span_note(value_a, "this value...")
31                            .span_note(value_b, "...conflicts with this value"));
32                    }
33
34                    return Err(value_b.error("field value conflicts with previous value")
35                        .span_help(variant_b, "...declared in this variant")
36                        .span_note(variant_a, "previous field with conflicting name"));
37                }
38
39                Ok(())
40            })
41        )
42        .outer_mapper(quote! {
43            #[allow(unused_imports)]
44            use #_http::uncased::AsUncased;
45        })
46        .inner_mapper(MapperBuild::new()
47            .with_output(|_, output| quote! {
48                fn from_value(
49                    __f: #_form::ValueField<'__v>
50                ) -> #_Result<Self, #_form::Errors<'__v>> {
51
52                    #output
53                }
54            })
55            .try_enum_map(|mapper, data| {
56                let mut variant_value = vec![];
57                for v in data.variants().map(|v| v.form_field_values()) {
58                    variant_value.append(&mut v?);
59                }
60
61                let variant_condition = data.variants()
62                    .map(|v| mapper.map_variant(v))
63                    .collect::<Result<Vec<_>>>()?;
64
65                let (_ok, _cow) = (std::iter::repeat(_Ok), std::iter::repeat(_Cow));
66                Ok(quote! {
67                    #(#variant_condition)*
68
69                    const OPTS: &'static [#_Cow<'static, str>] =
70                        &[#(#_cow::Borrowed(#variant_value)),*];
71
72                    let _error = #_form::Error::from(OPTS)
73                        .with_name(__f.name)
74                        .with_value(__f.value);
75
76                    #_Err(_error)?
77                })
78            })
79            .try_variant_map(|_, variant| {
80                let builder = variant.builder(|_| unreachable!("fieldless"));
81                let value = variant.form_field_values()?;
82
83                Ok(quote_spanned! { variant.span() =>
84                    if #(__f.value.as_uncased() == #value)||* {
85                        return #_Ok(#builder);
86                    }
87                })
88            })
89        )
90        .to_tokens()
91}