rocket_codegen/derive/
from_form_field.rs1use 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 .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}