rocket

Derive Macro FromForm

Source
#[derive(FromForm)]
{
    // Attributes available to this derive:
    #[form]
    #[field]
}
Expand description

Derive for the FromForm trait.

The FromForm derive can be applied to structures with named or unnamed fields:

use rocket::form::FromForm;

#[derive(FromForm)]
struct MyStruct<'r> {
    field: usize,
    #[field(name = "renamed_field")]
    #[field(name = uncased("RenamedField"))]
    other: &'r str,
    #[field(validate = range(1..), default = 3)]
    r#type: usize,
    #[field(default = None)]
    is_nice: bool,
}

#[derive(FromForm)]
#[field(validate = len(6..))]
#[field(validate = neq("password"))]
struct Password<'r>(&'r str);

Each field type is required to implement FromForm.

The derive generates an implementation of the FromForm trait.

Named Fields

If the structure has named fields, the implementation parses a form whose field names match the field names of the structure on which the derive was applied. Each field’s value is parsed with the FromForm implementation of the field’s type. The FromForm implementation succeeds only when all fields parse successfully or return a default. Errors are collected into a form::Errors and returned if non-empty after parsing all fields.

Unnamed Fields

If the structure is a tuple struct, it must have exactly one field. The implementation parses a form exactly when the internal field parses a form and any #[field] validations succeed.

§Syntax

The derive accepts one field attribute: field, and one container attribute, form, with the following syntax:

field := name? default? validate*

name := 'name' '=' name_val ','?
name_val :=  '"' FIELD_NAME '"'
         | 'uncased(' '"' FIELD_NAME '"' ')

default := 'default' '=' EXPR ','?
         | 'default_with' '=' EXPR ','?

validate := 'validate' '=' EXPR ','?

FIELD_NAME := valid field name, according to the HTML5 spec
EXPR := valid expression, as defined by Rust

#[field] can be applied any number of times on a field. default and default_with are mutually exclusive: at most one of default or default_with can be present per field.

#[derive(FromForm)]
struct MyStruct {
    #[field(name = uncased("number"))]
    #[field(default = 42)]
    field: usize,
    #[field(name = "renamed_field")]
    #[field(name = uncased("anotherName"))]
    #[field(validate = eq("banana"))]
    #[field(validate = neq("orange"))]
    other: String
}

For tuples structs, the field attribute can be applied to the structure itself:

#[derive(FromForm)]
#[field(default = 42, validate = eq(42))]
struct Meaning(usize);

§Field Attribute Parameters

  • name

    A name attribute changes the name to match against when parsing the form field. The value is either an exact string to match against ("foo"), or uncased("foo"), which causes the match to be case-insensitive but case-preserving. When more than one name attribute is applied, the field will match against any of the names.

  • validate = expr

    The validation expr is run if the field type parses successfully. The expression must return a value of type Result<(), form::Errors>. On Err, the errors are added to the thus-far collected errors. If more than one validate attribute is applied, all validations are run.

  • default = expr

    If expr is not literally None, the parameter sets the default value of the field to be expr.into(). If expr is None, the parameter unsets the default value of the field, if any. The expression is only evaluated if the attributed field is missing in the incoming form.

    Except when expr is None, expr must be of type T: Into<F> where F is the field’s type.

  • default_with = expr

    The parameter sets the default value of the field to be exactly expr which must be of type Option<F> where F is the field’s type. If the expression evaluates to None, there is no default. Otherwise the value wrapped in Some is used. The expression is only evaluated if the attributed field is missing in the incoming form.

    use std::num::NonZeroUsize;
    
    #[derive(FromForm)]
    struct MyForm {
        // `NonZeroUsize::new()` return an `Option<NonZeroUsize>`.
        #[field(default_with = NonZeroUsize::new(42))]
        num: NonZeroUsize,
    }

§Generics

The derive accepts any number of type generics and at most one lifetime generic. If a type generic is present, the generated implementation will require a bound of FromForm<'r> for the field type containing the generic. For example, for a struct struct Foo<T>(Json<T>), the bound Json<T>: FromForm<'r> will be added to the generated implementation.

use rocket::form::FromForm;
use rocket::serde::json::Json;

// The bounds `A: FromForm<'r>`, `B: FromForm<'r>` will be required.
#[derive(FromForm)]
struct FancyForm<A, B> {
    first: A,
    second: B,
};

// The bound `Json<T>: FromForm<'r>` will be required.
#[derive(FromForm)]
struct JsonToken<T> {
    token: Json<T>,
    id: usize,
}

If a lifetime generic is present, it is replaced with 'r in the generated implementation impl FromForm<'r>:

// Generates `impl<'r> FromForm<'r> for MyWrapper<'r>`.
#[derive(FromForm)]
struct MyWrapper<'a>(&'a str);

Both type generics and one lifetime generic may be used:

use rocket::form::{self, FromForm};

// The bound `form::Result<'r, T>: FromForm<'r>` will be required.
#[derive(FromForm)]
struct SomeResult<'o, T>(form::Result<'o, T>);

The special bounds on Json and Result are required due to incomplete and incorrect support for lifetime generics in async blocks in Rust. See rust-lang/#64552 for further details.