diff --git a/codegen/src/decorators/derive_form.rs b/codegen/src/decorators/derive_form.rs index 0c0408e7..4ae0d3ed 100644 --- a/codegen/src/decorators/derive_form.rs +++ b/codegen/src/decorators/derive_form.rs @@ -49,9 +49,38 @@ fn get_struct_lifetime(ecx: &mut ExtCtxt, item: &Annotatable, span: Span) } } -// TODO: Use proper logging to emit the error messages. +trait IgnoreExtraFieldStrategy { + fn shall_ignore() -> bool; +} + +struct IgnoreExtraField; +impl IgnoreExtraFieldStrategy for IgnoreExtraField { + fn shall_ignore() -> bool { + true + } +} + +struct ProhibitExtraField; +impl IgnoreExtraFieldStrategy for ProhibitExtraField { + fn shall_ignore() -> bool { + false + } +} + pub fn from_form_derive(ecx: &mut ExtCtxt, span: Span, meta_item: &MetaItem, annotated: &Annotatable, push: &mut FnMut(Annotatable)) { + from_form_derive_imp::(ecx, span, meta_item, annotated, push) +} + +pub fn from_form_ignorable_derive(ecx: &mut ExtCtxt, span: Span, meta_item: &MetaItem, + annotated: &Annotatable, push: &mut FnMut(Annotatable)) { + from_form_derive_imp::(ecx, span, meta_item, annotated, push) +} + +// TODO: Use proper logging to emit the error messages. +fn from_form_derive_imp + (ecx: &mut ExtCtxt, span: Span, meta_item: &MetaItem, + annotated: &Annotatable, push: &mut FnMut(Annotatable)) { let struct_lifetime = get_struct_lifetime(ecx, annotated, span); let (lifetime_var, trait_generics) = match struct_lifetime { lifetime@Some(_) => (lifetime, ty::LifetimeBounds::empty()), @@ -110,7 +139,7 @@ pub fn from_form_derive(ecx: &mut ExtCtxt, span: Span, meta_item: &MetaItem, ), attributes: vec![], is_unsafe: false, - combine_substructure: c_s(Box::new(from_form_substructure)), + combine_substructure: c_s(Box::new(from_form_substructure::)), unify_fieldless_variants: false, } ], @@ -122,7 +151,8 @@ pub fn from_form_derive(ecx: &mut ExtCtxt, span: Span, meta_item: &MetaItem, trait_def.expand(ecx, meta_item, annotated, push); } -fn from_form_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P { +fn from_form_substructure + (cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P { // Check that we specified the methods to the argument correctly. const EXPECTED_ARGS: usize = 1; let arg = if substr.nonself_args.len() == EXPECTED_ARGS { @@ -197,6 +227,9 @@ fn from_form_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substruct // The actual match statement. Iterate through all of the fields in the form // and use the $arms generated above. + + let shall_ignore = S::shall_ignore(); + stmts.push(quote_stmt!(cx, for (k, v) in $arg { match k.as_str() { @@ -207,9 +240,11 @@ fn from_form_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substruct * in sync with Rocket::preprocess. */ } _ => { - println!(" => {}={} has no matching field in struct.", - k, v); - $return_err_stmt + if !$shall_ignore { + println!(" => {}={} has no matching field in struct.", + k, v); + $return_err_stmt + } } }; } diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 1be81f2e..8d844959 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -182,7 +182,8 @@ pub fn plugin_registrar(reg: &mut Registry) { reg.register_macro("errors", macros::errors); register_derives!(reg, - "derive_FromForm" => from_form_derive + "derive_FromForm" => from_form_derive, + "derive_FromFormIgnorable" => from_form_ignorable_derive ); register_decorators!(reg, diff --git a/codegen/tests/run-pass/derive_form.rs b/codegen/tests/run-pass/derive_form.rs index 08c2756b..c8f0e692 100644 --- a/codegen/tests/run-pass/derive_form.rs +++ b/codegen/tests/run-pass/derive_form.rs @@ -12,6 +12,12 @@ struct TodoTask { completed: bool } +#[derive(Debug, PartialEq, FromFormIgnorable)] +struct TodoTaskIgnorable { + description: String, + completed: bool +} + // TODO: Make deriving `FromForm` for this enum possible. #[derive(Debug, PartialEq)] enum FormOption { @@ -94,6 +100,27 @@ fn main() { completed: false })); + // Same number of arguments: simple case. + let task: Option = parse("description=Hello&completed=on"); + assert_eq!(task, Some(TodoTaskIgnorable { + description: "Hello".to_string(), + completed: true + })); + + // Argument in string but not in form. + let task: Option = parse("other=a&description=Hello&completed=on"); + assert_eq!(task, Some(TodoTaskIgnorable { + description: "Hello".to_string(), + completed: true + })); + + // Ensure _method isn't required. + let task: Option = parse("_method=patch&description=Hello&completed=off"); + assert_eq!(task, Some(TodoTaskIgnorable { + description: "Hello".to_string(), + completed: false + })); + let form_string = &[ "password=testing", "checkbox=off", "checkbox=on", "number=10", "checkbox=off", "textarea=", "select=a", "radio=c",