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 131 132 133 134 135 136 137 138 139 140 141 142 143 144
use std::ops::{Deref, DerefMut};
use crate::form::prelude::*;
use crate::http::uri::fmt::{Query, FromUriParam};
/// A form guard for parsing form types strictly.
///
/// This type implements the [`FromForm`] trait and thus can be used as a
/// generic parameter to the [`Form`] data guard: `Form<Strict<T>>`, where `T`
/// implements `FromForm`. Unlike using `Form` directly, this type uses a
/// _strict_ parsing strategy: forms that contains a superset of the expected
/// fields (i.e, extra fields) will fail to parse and defaults will not be use
/// for missing fields.
///
/// # Strictness
///
/// A `Strict<T>` will parse successfully from an incoming form only if
/// the form contains the exact set of fields in `T`. Said another way, a
/// `Strict<T>` will error on missing and/or extra fields. For instance, if an
/// incoming form contains the fields "a", "b", and "c" while `T` only contains
/// "a" and "c", the form _will not_ parse as `Strict<T>`.
///
/// # Usage
///
/// `Strict<T>` implements [`FromForm`] as long as `T` implements `FromForm`. As
/// such, `Form<Strict<T>>` is a data guard:
///
/// ```rust
/// # #[macro_use] extern crate rocket;
/// use rocket::form::{Form, Strict};
///
/// #[derive(FromForm)]
/// struct UserInput {
/// value: String
/// }
///
/// #[post("/submit", data = "<user_input>")]
/// fn submit_task(user_input: Form<Strict<UserInput>>) -> String {
/// format!("Your value: {}", user_input.value)
/// }
/// ```
///
/// `Strict` can also be used to make individual fields strict while keeping the
/// overall structure and remaining fields lenient:
///
/// ```rust
/// # #[macro_use] extern crate rocket;
/// use rocket::form::{Form, Strict};
///
/// #[derive(FromForm)]
/// struct UserInput {
/// required: Strict<bool>,
/// uses_default: bool
/// }
/// ```
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Strict<T>(T);
impl<T> Strict<T> {
/// Consumes `self` and returns the inner value.
///
/// Note that since `Strict` implements [`Deref`] and [`DerefMut`] with
/// target `T`, reading and writing an inner value can be accomplished
/// transparently.
///
/// # Example
///
/// ```rust
/// # #[macro_use] extern crate rocket;
/// use rocket::form::{Form, Strict};
///
/// #[derive(FromForm)]
/// struct MyForm {
/// field: String,
/// }
///
/// #[post("/submit", data = "<form>")]
/// fn submit(form: Form<Strict<MyForm>>) -> String {
/// // We can read or mutate a value transparently:
/// let field: &str = &form.field;
///
/// // To gain ownership, however, use `into_inner()`:
/// form.into_inner().into_inner().field
/// }
/// ```
pub fn into_inner(self) -> T {
self.0
}
}
#[crate::async_trait]
impl<'v, T: FromForm<'v>> FromForm<'v> for Strict<T> {
type Context = T::Context;
#[inline(always)]
fn init(_: Options) -> Self::Context {
T::init(Options { strict: true })
}
#[inline(always)]
fn push_value(ctxt: &mut Self::Context, field: ValueField<'v>) {
T::push_value(ctxt, field)
}
#[inline(always)]
async fn push_data(ctxt: &mut Self::Context, field: DataField<'v, '_>) {
T::push_data(ctxt, field).await
}
#[inline(always)]
fn finalize(this: Self::Context) -> Result<'v, Self> {
T::finalize(this).map(Self)
}
}
impl<T> Deref for Strict<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for Strict<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T> From<T> for Strict<T> {
#[inline]
fn from(val: T) -> Strict<T> {
Strict(val)
}
}
impl<'f, A, T: FromUriParam<Query, A> + FromForm<'f>> FromUriParam<Query, A> for Strict<T> {
type Target = T::Target;
#[inline(always)]
fn from_uri_param(param: A) -> Self::Target {
T::from_uri_param(param)
}
}