Derive Macro rocket_codegen::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"
), oruncased("foo")
, which causes the match to be case-insensitive but case-preserving. When more than onename
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 typeResult<(), form::Errors>
. OnErr
, the errors are added to the thus-far collected errors. If more than onevalidate
attribute is applied, all validations are run. -
default = expr
If
expr
is not literallyNone
, the parameter sets the default value of the field to beexpr.into()
. Ifexpr
isNone
, 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
isNone
,expr
must be of typeT: Into<F>
whereF
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 typeOption<F>
whereF
is the field’s type. If the expression evaluates toNone
, there is no default. Otherwise the value wrapped inSome
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.