1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use proc_macro::{Span, TokenStream};
use devise::{*, ext::{TypeExt, Split3}};

#[derive(FromMeta)]
crate struct Form {
    crate field: FormField,
}

crate struct FormField {
    crate span: Span,
    crate name: String
}

fn is_valid_field_name(s: &str) -> bool {
    // The HTML5 spec (4.10.18.1) says 'isindex' is not allowed.
    if s == "isindex" || s.is_empty() {
        return false
    }

    // We allow all visible ASCII characters except '&', '=', and '?' since we
    // use those as control characters for parsing.
    s.chars().all(|c| (c >= ' ' && c <= '~') && c != '&' && c != '=' && c != '?')
}

impl FromMeta for FormField {
    fn from_meta(meta: MetaItem) -> Result<Self> {
        let string = String::from_meta(meta)?;
        if !is_valid_field_name(&string) {
            return Err(meta.value_span().error("invalid form field name"));
        }

        Ok(FormField { span: meta.value_span(), name: string })
    }
}

fn validate_struct(gen: &DeriveGenerator, data: Struct) -> Result<()> {
    if data.fields().is_empty() {
        return Err(gen.input.span().error("at least one field is required"));
    }

    let mut names = ::std::collections::HashMap::new();
    for field in data.fields().iter() {
        let id = field.ident.as_ref().expect("named field");
        let field = match Form::from_attrs("form", &field.attrs) {
            Some(result) => result?.field,
            None => FormField { span: Spanned::span(&id), name: id.to_string() }
        };

        if let Some(span) = names.get(&field.name) {
            return Err(field.span.error("duplicate field name")
                       .span_note(*span, "previous definition here"));
        }

        names.insert(field.name, field.span);
    }

    Ok(())
}

pub fn derive_from_form(input: TokenStream) -> TokenStream {
    let form_error = quote!(::rocket::request::FormParseError);
    DeriveGenerator::build_for(input, quote!(impl<'__f> ::rocket::request::FromForm<'__f>))
        .generic_support(GenericSupport::Lifetime | GenericSupport::Type)
        .replace_generic(0, 0)
        .data_support(DataSupport::NamedStruct)
        .map_type_generic(|_, ident, _| quote! {
            #ident : ::rocket::request::FromFormValue<'__f>
        })
        .validate_generics(|_, generics| match generics.lifetimes().count() > 1 {
            true => Err(generics.span().error("only one lifetime is supported")),
            false => Ok(())
        })
        .validate_struct(validate_struct)
        .function(|_, inner| quote! {
            type Error = ::rocket::request::FormParseError<'__f>;

            fn from_form(
                __items: &mut ::rocket::request::FormItems<'__f>,
                __strict: bool,
            ) -> ::std::result::Result<Self, Self::Error> {
                #inner
            }
        })
        .try_map_fields(move |_, fields| {
            define_vars_and_mods!(_None, _Some, _Ok, _Err);
            let (constructors, matchers, builders) = fields.iter().map(|field| {
                let (ident, span) = (&field.ident, field.span().into());
                let default_name = ident.as_ref().expect("named").to_string();
                let name = Form::from_attrs("form", &field.attrs)
                    .map(|result| result.map(|form| form.field.name))
                    .unwrap_or_else(|| Ok(default_name))?;

                let ty = field.ty.with_stripped_lifetimes();
                let ty = quote_spanned! {
                    span => <#ty as ::rocket::request::FromFormValue>
                };

                let constructor = quote_spanned!(span => let mut #ident = #_None;);

                let matcher = quote_spanned! { span =>
                    #name => { #ident = #_Some(#ty::from_form_value(__v)
                                .map_err(|_| #form_error::BadValue(__k, __v))?); },
                };

                let builder = quote_spanned! { span =>
                    #ident: #ident.or_else(#ty::default)
                        .ok_or_else(|| #form_error::Missing(#name.into()))?,
                };

                Ok((constructor, matcher, builder))
            }).collect::<Result<Vec<_>>>()?.into_iter().split3();

            Ok(quote! {
                #(#constructors)*

                for (__k, __v) in __items.map(|item| item.key_value()) {
                    match __k.as_str() {
                        #(#matchers)*
                        _ if __strict && __k != "_method" => {
                            return #_Err(#form_error::Unknown(__k, __v));
                        }
                        _ => { /* lenient or "method"; let it pass */ }
                    }
                }

                #_Ok(Self { #(#builders)* })
            })
        })
        .to_tokens()
}