Rename 'FromDataSimple' to 'FromData'. Make async.

The 'FromData' trait becomes 'FromTransformedData'.
This commit is contained in:
Sergio Benitez 2020-07-12 02:23:00 -07:00
parent 854e90d39f
commit 33e95f4900
16 changed files with 191 additions and 179 deletions

View File

@ -22,7 +22,7 @@ use tokio::io::AsyncReadExt;
use rocket::request::Request;
use rocket::outcome::Outcome::*;
use rocket::data::{Transform::*, Transformed, Data, FromData, TransformFuture, FromDataFuture};
use rocket::data::{Transform::*, Transformed, Data, FromTransformedData, TransformFuture, FromDataFuture};
use rocket::response::{self, Responder, content};
use rocket::http::Status;
@ -32,7 +32,7 @@ use serde::de::{Deserialize, Deserializer};
#[doc(hidden)]
pub use serde_json::{json_internal, json_internal_vec};
/// The JSON type: implements [`FromData`] and [`Responder`], allowing you to
/// The JSON type: implements [`FromTransformedData`] and [`Responder`], allowing you to
/// easily consume and respond with JSON.
///
/// ## Receiving JSON
@ -128,7 +128,7 @@ pub enum JsonError<'a> {
Parse(&'a str, serde_json::error::Error),
}
impl<'a, T: Deserialize<'a>> FromData<'a> for Json<T> {
impl<'a, T: Deserialize<'a>> FromTransformedData<'a> for Json<T> {
type Error = JsonError<'a>;
type Owned = String;
type Borrowed = str;

View File

@ -20,7 +20,7 @@ use tokio::io::AsyncReadExt;
use rocket::request::Request;
use rocket::outcome::Outcome::*;
use rocket::data::{Data, FromData, FromDataFuture, Transform::*, TransformFuture, Transformed};
use rocket::data::{Data, FromTransformedData, FromDataFuture, Transform::*, TransformFuture, Transformed};
use rocket::http::Status;
use rocket::response::{self, content, Responder};
@ -29,7 +29,7 @@ use serde::de::Deserialize;
pub use rmp_serde::decode::Error;
/// The `MsgPack` type: implements [`FromData`] and [`Responder`], allowing you
/// The `MsgPack` type: implements [`FromTransformedData`] and [`Responder`], allowing you
/// to easily consume and respond with MessagePack data.
///
/// ## Receiving MessagePack
@ -113,7 +113,7 @@ impl<T> MsgPack<T> {
/// Default limit for MessagePack is 1MB.
const LIMIT: u64 = 1 << 20;
impl<'a, T: Deserialize<'a>> FromData<'a> for MsgPack<T> {
impl<'a, T: Deserialize<'a>> FromTransformedData<'a> for MsgPack<T> {
type Error = Error;
type Owned = Vec<u8>;
type Borrowed = [u8];

View File

@ -175,10 +175,10 @@ fn param_expr(seg: &Segment, ident: &syn::Ident, ty: &syn::Type) -> TokenStream2
}
fn data_expr(ident: &syn::Ident, ty: &syn::Type) -> TokenStream2 {
define_vars_and_mods!(req, data, FromData, Outcome, Transform);
define_vars_and_mods!(req, data, FromTransformedData, Outcome, Transform);
let span = ident.span().unstable().join(ty.span()).unwrap().into();
quote_spanned! { span =>
let __transform = <#ty as #FromData>::transform(#req, #data).await;
let __transform = <#ty as #FromTransformedData>::transform(#req, #data).await;
#[allow(unreachable_patterns, unreachable_code)]
let __outcome = match __transform {
@ -195,7 +195,7 @@ fn data_expr(ident: &syn::Ident, ty: &syn::Type) -> TokenStream2 {
};
#[allow(non_snake_case, unreachable_patterns, unreachable_code)]
let #ident: #ty = match <#ty as #FromData>::from_data(#req, __outcome).await {
let #ident: #ty = match <#ty as #FromTransformedData>::from_data(#req, __outcome).await {
#Outcome::Success(__d) => __d,
#Outcome::Forward(__d) => return #Outcome::Forward(__d),
#Outcome::Failure((__c, _)) => return #Outcome::Failure(__c),

View File

@ -84,7 +84,7 @@ vars_and_mods! {
handler => rocket::handler,
log => rocket::logger,
Outcome => rocket::Outcome,
FromData => rocket::data::FromData,
FromTransformedData => rocket::data::FromTransformedData,
Transform => rocket::data::Transform,
Query => rocket::request::Query,
Request => rocket::Request,
@ -252,7 +252,7 @@ macro_rules! route_attribute {
/// | path | `<ident..>` | [`FromSegments`] |
/// | query | `<ident>` | [`FromFormValue`] |
/// | query | `<ident..>` | [`FromQuery`] |
/// | data | `<ident>` | [`FromData`] |
/// | data | `<ident>` | [`FromTransformedData`] |
///
/// The type of each function argument that _does not_ have a
/// corresponding dynamic parameter is required to implement the
@ -265,7 +265,7 @@ macro_rules! route_attribute {
/// [`FromSegments`]: ../rocket/request/trait.FromSegments.html
/// [`FromFormValue`]: ../rocket/request/trait.FromFormValue.html
/// [`FromQuery`]: ../rocket/request/trait.FromQuery.html
/// [`FromData`]: ../rocket/data/trait.FromData.html
/// [`FromTransformedData`]: ../rocket/data/trait.FromTransformedData.html
/// [`FromRequest`]: ../rocket/request/trait.FromRequest.html
/// [`Route`]: ../rocket/struct.Route.html
/// [`Responder`]: ../rocket/response/trait.Responder.html
@ -297,7 +297,7 @@ macro_rules! route_attribute {
///
/// If a data guard fails, the request is forwarded if the
/// [`Outcome`] is `Forward` or failed if the [`Outcome`] is
/// `Failure`. See [`FromData` Outcomes] for further detail.
/// `Failure`. See [`FromTransformedData` Outcomes] for further detail.
///
/// If all validation succeeds, the decorated function is called.
/// The returned value is used to generate a [`Response`] via the
@ -320,7 +320,7 @@ macro_rules! route_attribute {
/// [`Outcome`]: ../rocket/enum.Outcome.html
/// [`Response`]: ../rocket/struct.Response.html
/// [`FromRequest` Outcomes]: ../rocket/request/trait.FromRequest.html#outcomes
/// [`FromData` Outcomes]: ../rocket/data/trait.FromData.html#outcomes
/// [`FromTransformedData` Outcomes]: ../rocket/data/trait.FromTransformedData.html#outcomes
#[proc_macro_attribute]
pub fn $name(args: TokenStream, input: TokenStream) -> TokenStream {
emit!(attribute::route::route_attribute($method, args, input))

View File

@ -3,7 +3,7 @@
use rocket::{Request, Data, Outcome::*};
use rocket::local::blocking::Client;
use rocket::request::Form;
use rocket::data::{self, FromDataSimple};
use rocket::data::{self, FromData};
use rocket::http::{RawStr, ContentType, Status};
use rocket::tokio::io::AsyncReadExt;
@ -16,19 +16,18 @@ struct Inner<'r> {
struct Simple(String);
impl FromDataSimple for Simple {
#[async_trait]
impl FromData for Simple {
type Error = ();
fn from_data(_: &Request<'_>, data: Data) -> data::FromDataFuture<'static, Self, ()> {
Box::pin(async {
let mut string = String::new();
let mut stream = data.open().take(64);
if let Err(_) = stream.read_to_string(&mut string).await {
return Failure((Status::InternalServerError, ()));
}
async fn from_data(_: &Request<'_>, data: Data) -> data::Outcome<Self, ()> {
let mut string = String::new();
let mut stream = data.open().take(64);
if let Err(_) = stream.read_to_string(&mut string).await {
return Failure((Status::InternalServerError, ()));
}
Success(Simple(string))
})
Success(Simple(string))
}
}

View File

@ -10,7 +10,7 @@ use std::path::PathBuf;
use rocket::{Request, Outcome::*};
use rocket::http::ext::Normalize;
use rocket::local::blocking::Client;
use rocket::data::{self, Data, FromDataSimple};
use rocket::data::{self, Data, FromData};
use rocket::request::Form;
use rocket::http::{Status, RawStr, ContentType};
use rocket::tokio::io::AsyncReadExt;
@ -24,16 +24,15 @@ struct Inner<'r> {
struct Simple(String);
impl FromDataSimple for Simple {
#[async_trait]
impl FromData for Simple {
type Error = ();
fn from_data(_: &Request<'_>, data: Data) -> data::FromDataFuture<'static, Self, ()> {
Box::pin(async move {
let mut string = String::new();
let mut stream = data.open().take(64);
stream.read_to_string(&mut string).await.unwrap();
Success(Simple(string))
})
async fn from_data(_: &Request<'_>, data: Data) -> data::Outcome<Self, ()> {
let mut string = String::new();
let mut stream = data.open().take(64);
stream.read_to_string(&mut string).await.unwrap();
Success(Simple(string))
}
}

View File

@ -22,13 +22,13 @@ error[E0277]: the trait bound `Q: rocket::request::FromQuery<'_>` is not satisfi
15 | fn f3(foo: Q) {} //~ ERROR FromQuery
| ^^^^^^ the trait `rocket::request::FromQuery<'_>` is not implemented for `Q`
error[E0277]: the trait bound `Q: rocket::data::FromDataSimple` is not satisfied
error[E0277]: the trait bound `Q: rocket::data::FromData` is not satisfied
--> $DIR/route-type-errors.rs:18:7
|
18 | fn f4(foo: Q) {} //~ ERROR FromData
| ^^^^^^ the trait `rocket::data::FromDataSimple` is not implemented for `Q`
| ^^^^^^ the trait `rocket::data::FromData` is not implemented for `Q`
|
= note: required because of the requirements on the impl of `rocket::data::FromData<'_>` for `Q`
= note: required because of the requirements on the impl of `rocket::data::FromTransformedData<'_>` for `Q`
error[E0277]: the trait bound `Q: rocket::request::FromRequest<'_, '_>` is not satisfied
--> $DIR/route-type-errors.rs:21:7

View File

@ -16,7 +16,7 @@ const PEEK_BYTES: usize = 512;
///
/// This type is the only means by which the body of a request can be retrieved.
/// This type is not usually used directly. Instead, types that implement
/// [`FromData`](crate::data::FromData) are used via code generation by
/// [`FromTransformedData`](crate::data::FromTransformedData) are used via code generation by
/// specifying the `data = "<var>"` route parameter as follows:
///
/// ```rust
@ -27,8 +27,8 @@ const PEEK_BYTES: usize = 512;
/// # fn main() { }
/// ```
///
/// Above, `DataGuard` can be any type that implements `FromData`. Note that
/// `Data` itself implements `FromData`.
/// Above, `DataGuard` can be any type that implements `FromTransformedData`. Note that
/// `Data` itself implements `FromTransformedData`.
///
/// # Reading Data
///

View File

@ -9,7 +9,7 @@ use crate::http::Status;
use crate::request::Request;
use crate::data::Data;
/// Type alias for the `Outcome` of a `FromData` conversion.
/// Type alias for the `Outcome` of a `FromTransformedData` conversion.
pub type Outcome<S, E> = outcome::Outcome<S, (Status, E), Data>;
impl<S, E> IntoOutcome<S, (Status, E), Data> for Result<S, E> {
@ -36,14 +36,14 @@ impl<S, E> IntoOutcome<S, (Status, E), Data> for Result<S, E> {
/// Indicates how incoming data should be transformed before being parsed and
/// validated by a data guard.
///
/// See the documentation for [`FromData`] for usage details.
/// See the documentation for [`FromTransformedData`] for usage details.
pub enum Transform<T, B = T> {
/// Indicates that data should be or has been transformed into the
/// [`FromData::Owned`] variant.
/// [`FromTransformedData::Owned`] variant.
Owned(T),
/// Indicates that data should be or has been transformed into the
/// [`FromData::Borrowed`] variant.
/// [`FromTransformedData::Borrowed`] variant.
Borrowed(B)
}
@ -92,29 +92,29 @@ impl<T, B> Transform<T, B> {
}
}
/// Type alias to the `outcome` input type of [`FromData::from_data`].
/// Type alias to the `outcome` input type of [`FromTransformedData::from_data`].
///
/// This is a hairy type, but the gist is that this is a [`Transform`] where,
/// for a given `T: FromData`:
/// for a given `T: FromTransformedData`:
///
/// * The `Owned` variant is an `Outcome` whose `Success` value is of type
/// [`FromData::Owned`].
/// [`FromTransformedData::Owned`].
///
/// * The `Borrowed` variant is an `Outcome` whose `Success` value is a borrow
/// of type [`FromData::Borrowed`].
/// of type [`FromTransformedData::Borrowed`].
///
/// * In either case, the `Outcome`'s `Failure` variant is a value of type
/// [`FromData::Error`].
/// [`FromTransformedData::Error`].
pub type Transformed<'a, T> =
Transform<
Outcome<<T as FromData<'a>>::Owned, <T as FromData<'a>>::Error>,
Outcome<&'a <T as FromData<'a>>::Borrowed, <T as FromData<'a>>::Error>
Outcome<<T as FromTransformedData<'a>>::Owned, <T as FromTransformedData<'a>>::Error>,
Outcome<&'a <T as FromTransformedData<'a>>::Borrowed, <T as FromTransformedData<'a>>::Error>
>;
/// Type alias to the `Future` returned by [`FromData::transform`].
/// Type alias to the `Future` returned by [`FromTransformedData::transform`].
pub type TransformFuture<'fut, T, E> = BoxFuture<'fut, Transform<Outcome<T, E>>>;
/// Type alias to the `Future` returned by [`FromData::from_data`].
/// Type alias to the `Future` returned by [`FromTransformedData::from_data`].
pub type FromDataFuture<'fut, T, E> = BoxFuture<'fut, Outcome<T, E>>;
/// Trait implemented by data guards to derive a value from request body data.
@ -123,16 +123,16 @@ pub type FromDataFuture<'fut, T, E> = BoxFuture<'fut, Outcome<T, E>>;
///
/// A data guard is a [request guard] that operates on a request's body data.
/// Data guards validate, parse, and optionally convert request body data.
/// Validation and parsing/conversion is implemented through `FromData`. In
/// other words, every type that implements `FromData` is a data guard.
/// Validation and parsing/conversion is implemented through `FromTransformedData`. In
/// other words, every type that implements `FromTransformedData` is a data guard.
///
/// Data guards are used as the target of the `data` route attribute parameter.
/// A handler can have at most one data guard.
///
/// For many data guards, implementing [`FromDataSimple`] will be simpler and
/// sufficient. All types that implement `FromDataSimple` automatically
/// implement `FromData`. Thus, when possible, prefer to implement
/// [`FromDataSimple`] instead of `FromData`.
/// For many data guards, implementing [`FromData`] will be simpler and
/// sufficient. All types that implement `FromData` automatically
/// implement `FromTransformedData`. Thus, when possible, prefer to implement
/// [`FromData`] instead of `FromTransformedData`.
///
/// [request guard]: crate::request::FromRequest
///
@ -140,7 +140,7 @@ pub type FromDataFuture<'fut, T, E> = BoxFuture<'fut, Outcome<T, E>>;
///
/// In the example below, `var` is used as the argument name for the data guard
/// type `DataGuard`. When the `submit` route matches, Rocket will call the
/// `FromData` implementation for the type `T`. The handler will only be called
/// `FromTransformedData` implementation for the type `T`. The handler will only be called
/// if the guard returns successfully.
///
/// ```rust
@ -154,22 +154,22 @@ pub type FromDataFuture<'fut, T, E> = BoxFuture<'fut, Outcome<T, E>>;
/// # Transforming
///
/// Data guards can optionally _transform_ incoming data before processing it
/// via an implementation of the [`FromData::transform()`] method. This is
/// via an implementation of the [`FromTransformedData::transform()`] method. This is
/// useful when a data guard requires or could benefit from a reference to body
/// data as opposed to an owned version. If a data guard has no need to operate
/// on a reference to body data, [`FromDataSimple`] should be implemented
/// on a reference to body data, [`FromData`] should be implemented
/// instead; it is simpler to implement and less error prone. All types that
/// implement `FromDataSimple` automatically implement `FromData`.
/// implement `FromData` automatically implement `FromTransformedData`.
///
/// When exercising a data guard, Rocket first calls the guard's
/// [`FromData::transform()`] method and awaits on the returned future, then
/// calls the guard's [`FromData::from_data()`] method and awaits on that
/// returned future. Rocket stores data returned by [`FromData::transform()`] on
/// [`FromTransformedData::transform()`] method and awaits on the returned future, then
/// calls the guard's [`FromTransformedData::from_data()`] method and awaits on that
/// returned future. Rocket stores data returned by [`FromTransformedData::transform()`] on
/// the stack. If `transform` returns a [`Transform::Owned`], Rocket moves the
/// data back to the data guard in the subsequent `from_data` call as a
/// `Transform::Owned`. If instead `transform` returns a [`Transform::Borrowed`]
/// variant, Rocket calls `borrow()` on the owned value, producing a borrow of
/// the associated [`FromData::Borrowed`] type and passing it as a
/// the associated [`FromTransformedData::Borrowed`] type and passing it as a
/// `Transform::Borrowed`.
///
/// ## Example
@ -198,7 +198,7 @@ pub type FromDataFuture<'fut, T, E> = BoxFuture<'fut, Outcome<T, E>>;
/// use tokio::io::AsyncReadExt;
///
/// use rocket::{Request, Data, Outcome::*};
/// use rocket::data::{FromData, Outcome, Transform, Transformed, TransformFuture, FromDataFuture};
/// use rocket::data::{FromTransformedData, Outcome, Transform, Transformed, TransformFuture, FromDataFuture};
/// use rocket::http::Status;
///
/// const NAME_LIMIT: u64 = 256;
@ -208,7 +208,7 @@ pub type FromDataFuture<'fut, T, E> = BoxFuture<'fut, Outcome<T, E>>;
/// Parse
/// }
///
/// impl<'a> FromData<'a> for Name<'a> {
/// impl<'a> FromTransformedData<'a> for Name<'a> {
/// type Error = NameError;
/// type Owned = String;
/// type Borrowed = str;
@ -279,7 +279,7 @@ pub type FromDataFuture<'fut, T, E> = BoxFuture<'fut, Outcome<T, E>>;
///
/// # Provided Implementations
///
/// Rocket implements `FromData` for several built-in types. Their behavior is
/// Rocket implements `FromTransformedData` for several built-in types. Their behavior is
/// documented here.
///
/// * **Data**
@ -288,24 +288,24 @@ pub type FromDataFuture<'fut, T, E> = BoxFuture<'fut, Outcome<T, E>>;
///
/// _This implementation always returns successfully._
///
/// * **Option&lt;T>** _where_ **T: FromData**
/// * **Option&lt;T>** _where_ **T: FromTransformedData**
///
/// The type `T` is derived from the incoming data using `T`'s `FromData`
/// The type `T` is derived from the incoming data using `T`'s `FromTransformedData`
/// implementation. If the derivation is a `Success`, the derived value is
/// returned in `Some`. Otherwise, a `None` is returned.
///
/// _This implementation always returns successfully._
///
/// * **Result&lt;T, T::Error>** _where_ **T: FromData**
/// * **Result&lt;T, T::Error>** _where_ **T: FromTransformedData**
///
/// The type `T` is derived from the incoming data using `T`'s `FromData`
/// The type `T` is derived from the incoming data using `T`'s `FromTransformedData`
/// implementation. If derivation is a `Success`, the value is returned in
/// `Ok`. If the derivation is a `Failure`, the error value is returned in
/// `Err`. If the derivation is a `Forward`, the request is forwarded.
///
/// * **String**
///
/// **Note:** _An implementation of `FromData` for `String` is only available
/// **Note:** _An implementation of `FromTransformedData` for `String` is only available
/// when compiling in debug mode!_
///
/// Reads the entire request body into a `String`. If reading fails, returns
@ -318,7 +318,7 @@ pub type FromDataFuture<'fut, T, E> = BoxFuture<'fut, Outcome<T, E>>;
///
/// * **Vec&lt;u8>**
///
/// **Note:** _An implementation of `FromData` for `Vec<u8>` is only
/// **Note:** _An implementation of `FromTransformedData` for `Vec<u8>` is only
/// available when compiling in debug mode!_
///
/// Reads the entire request body into a `Vec<u8>`. If reading fails,
@ -329,24 +329,24 @@ pub type FromDataFuture<'fut, T, E> = BoxFuture<'fut, Outcome<T, E>>;
/// memory; since the user controls the size of the body, this is an obvious
/// vector for a denial of service attack.
///
/// # Simplified `FromData`
/// # Simplified `FromTransformedData`
///
/// For an example of a type that wouldn't require transformation, see the
/// [`FromDataSimple`] documentation.
pub trait FromData<'a>: Sized {
/// [`FromData`] documentation.
pub trait FromTransformedData<'a>: Sized {
/// The associated error to be returned when the guard fails.
type Error: Send;
/// The owned type returned from [`FromData::transform()`].
/// The owned type returned from [`FromTransformedData::transform()`].
///
/// The trait bounds ensures that it is is possible to borrow an
/// `&Self::Borrowed` from a value of this type.
type Owned: Borrow<Self::Borrowed>;
/// The _borrowed_ type consumed by [`FromData::from_data()`] when
/// [`FromData::transform()`] returns a [`Transform::Borrowed`].
/// The _borrowed_ type consumed by [`FromTransformedData::from_data()`] when
/// [`FromTransformedData::transform()`] returns a [`Transform::Borrowed`].
///
/// If [`FromData::from_data()`] returns a [`Transform::Owned`], this
/// If [`FromTransformedData::from_data()`] returns a [`Transform::Owned`], this
/// associated type should be set to `Self::Owned`.
type Borrowed: ?Sized;
@ -385,14 +385,14 @@ pub trait FromData<'a>: Sized {
///
/// ```rust
/// # #[macro_use] extern crate rocket;
/// # use rocket::data::{Data, FromData, Transformed, Outcome};
/// # fn f<'a>(outcome: Transformed<'a, Data>) -> Outcome<Data, <Data as FromData<'a>>::Error> {
/// # use rocket::data::{Data, FromTransformedData, Transformed, Outcome};
/// # fn f<'a>(outcome: Transformed<'a, Data>) -> Outcome<Data, <Data as FromTransformedData<'a>>::Error> {
/// // If `Owned` was returned from `transform`:
/// let data = try_outcome!(outcome.owned());
/// # unimplemented!()
/// # }
///
/// # fn g<'a>(outcome: Transformed<'a, Data>) -> Outcome<Data, <Data as FromData<'a>>::Error> {
/// # fn g<'a>(outcome: Transformed<'a, Data>) -> Outcome<Data, <Data as FromTransformedData<'a>>::Error> {
/// // If `Borrowed` was returned from `transform`:
/// let data = try_outcome!(outcome.borrowed());
/// # unimplemented!()
@ -401,8 +401,8 @@ pub trait FromData<'a>: Sized {
fn from_data(request: &'a Request<'_>, outcome: Transformed<'a, Self>) -> FromDataFuture<'a, Self, Self::Error>;
}
/// The identity implementation of `FromData`. Always returns `Success`.
impl<'a> FromData<'a> for Data {
/// The identity implementation of `FromTransformedData`. Always returns `Success`.
impl<'a> FromTransformedData<'a> for Data {
type Error = std::convert::Infallible;
type Owned = Data;
type Borrowed = ();
@ -418,12 +418,34 @@ impl<'a> FromData<'a> for Data {
}
}
/// A simple, less complex variant of [`FromData`].
/// A simple, less complex variant of [`FromTransformedData`].
///
/// When transformation of incoming data isn't required, data guards should
/// implement this trait instead of [`FromData`]. Any type that implements
/// `FromDataSimple` automatically implements `FromData`. For a description of
/// data guards, see the [`FromData`] documentation.
/// implement this trait instead of [`FromTransformedData`]. Any type that implements
/// `FromData` automatically implements `FromTransformedData`. For a description of
/// data guards, see the [`FromTransformedData`] documentation.
///
/// ## Async Trait
///
/// [`FromData`] is an _async_ trait. Implementations of `FromData` must
/// be decorated with an attribute of `#[rocket::async_trait]`:
///
/// ```rust
/// use rocket::request::Request;
/// use rocket::data::{self, Data, FromData};
/// # struct MyType;
/// # type MyError = String;
///
/// #[rocket::async_trait]
/// impl FromData for MyType {
/// type Error = MyError;
///
/// async fn from_data(req: &Request<'_>, data: Data) -> data::Outcome<Self, MyError> {
/// /* .. */
/// # unimplemented!()
/// }
/// }
/// ```
///
/// # Example
///
@ -438,7 +460,7 @@ impl<'a> FromData<'a> for Data {
///
/// `Person` has a custom serialization format, so the built-in `Json` type
/// doesn't suffice. The format is `<name>:<age>` with `Content-Type:
/// application/x-person`. You'd like to use `Person` as a `FromData` type so
/// application/x-person`. You'd like to use `Person` as a `FromTransformedData` type so
/// that you can retrieve it directly from a client's request body:
///
/// ```rust
@ -450,7 +472,7 @@ impl<'a> FromData<'a> for Data {
/// }
/// ```
///
/// A `FromDataSimple` implementation allowing this looks like:
/// A `FromData` implementation allowing this looks like:
///
/// ```rust
/// # #[macro_use] extern crate rocket;
@ -460,48 +482,46 @@ impl<'a> FromData<'a> for Data {
/// #
/// use std::io::Read;
///
/// use tokio::io::AsyncReadExt;
///
/// use rocket::{Request, Data, Outcome, Outcome::*};
/// use rocket::data::{self, FromDataSimple, FromDataFuture};
/// use rocket::data::{self, FromData, FromDataFuture};
/// use rocket::http::{Status, ContentType};
/// use rocket::tokio::io::AsyncReadExt;
///
/// // Always use a limit to prevent DoS attacks.
/// const LIMIT: u64 = 256;
///
/// impl FromDataSimple for Person {
/// #[rocket::async_trait]
/// impl FromData for Person {
/// type Error = String;
///
/// fn from_data(req: &Request, data: Data) -> FromDataFuture<'static, Self, String> {
/// async fn from_data(req: &Request<'_>, data: Data) -> data::Outcome<Self, String> {
/// // Ensure the content type is correct before opening the data.
/// let person_ct = ContentType::new("application", "x-person");
/// if req.content_type() != Some(&person_ct) {
/// return Box::pin(async move { Outcome::Forward(data) });
/// return Forward(data);
/// }
///
/// Box::pin(async move {
/// // Read the data into a String.
/// let mut string = String::new();
/// let mut reader = data.open().take(LIMIT);
/// if let Err(e) = reader.read_to_string(&mut string).await {
/// return Failure((Status::InternalServerError, format!("{:?}", e)));
/// }
/// // Read the data into a String.
/// let mut string = String::new();
/// let mut reader = data.open().take(LIMIT);
/// if let Err(e) = reader.read_to_string(&mut string).await {
/// return Failure((Status::InternalServerError, format!("{:?}", e)));
/// }
///
/// // Split the string into two pieces at ':'.
/// let (name, age) = match string.find(':') {
/// Some(i) => (string[..i].to_string(), &string[(i + 1)..]),
/// None => return Failure((Status::UnprocessableEntity, "':'".into()))
/// };
/// // Split the string into two pieces at ':'.
/// let (name, age) = match string.find(':') {
/// Some(i) => (string[..i].to_string(), &string[(i + 1)..]),
/// None => return Failure((Status::UnprocessableEntity, "':'".into()))
/// };
///
/// // Parse the age.
/// let age: u16 = match age.parse() {
/// Ok(age) => age,
/// Err(_) => return Failure((Status::UnprocessableEntity, "Age".into()))
/// };
/// // Parse the age.
/// let age: u16 = match age.parse() {
/// Ok(age) => age,
/// Err(_) => return Failure((Status::UnprocessableEntity, "Age".into()))
/// };
///
/// // Return successfully.
/// Success(Person { name, age })
/// })
/// // Return successfully.
/// Success(Person { name, age })
/// }
/// }
/// # #[post("/person", data = "<person>")]
@ -510,7 +530,8 @@ impl<'a> FromData<'a> for Data {
/// # fn person2(person: Result<Person, String>) { }
/// # fn main() { }
/// ```
pub trait FromDataSimple: Sized {
#[crate::async_trait]
pub trait FromData: Sized {
/// The associated error to be returned when the guard fails.
type Error: Send + 'static;
@ -520,10 +541,10 @@ pub trait FromDataSimple: Sized {
/// If validation and parsing succeeds, an outcome of `Success` is returned.
/// If the data is not appropriate given the type of `Self`, `Forward` is
/// returned. If parsing fails, `Failure` is returned.
fn from_data(request: &Request<'_>, data: Data) -> FromDataFuture<'static, Self, Self::Error>;
async fn from_data(request: &Request<'_>, data: Data) -> Outcome<Self, Self::Error>;
}
impl<'a, T: FromDataSimple + 'a> FromData<'a> for T {
impl<'a, T: FromData + 'a> FromTransformedData<'a> for T {
type Error = T::Error;
type Owned = Data;
type Borrowed = ();
@ -542,7 +563,7 @@ impl<'a, T: FromDataSimple + 'a> FromData<'a> for T {
}
}
impl<'a, T: FromData<'a> + 'a> FromData<'a> for Result<T, T::Error> {
impl<'a, T: FromTransformedData<'a> + 'a> FromTransformedData<'a> for Result<T, T::Error> {
type Error = T::Error;
type Owned = T::Owned;
type Borrowed = T::Borrowed;
@ -562,7 +583,7 @@ impl<'a, T: FromData<'a> + 'a> FromData<'a> for Result<T, T::Error> {
}
}
impl<'a, T: FromData<'a> + 'a> FromData<'a> for Option<T> {
impl<'a, T: FromTransformedData<'a> + 'a> FromTransformedData<'a> for Option<T> {
type Error = T::Error;
type Owned = T::Owned;
type Borrowed = T::Borrowed;
@ -582,37 +603,37 @@ impl<'a, T: FromData<'a> + 'a> FromData<'a> for Option<T> {
}
#[cfg(debug_assertions)]
impl FromDataSimple for String {
#[crate::async_trait]
impl FromData for String {
type Error = std::io::Error;
#[inline(always)]
fn from_data(_: &Request<'_>, data: Data) -> FromDataFuture<'static, Self, Self::Error> {
async fn from_data(_: &Request<'_>, data: Data) -> Outcome<Self, Self::Error> {
use tokio::io::AsyncReadExt;
Box::pin(async {
let mut string = String::new();
let mut reader = data.open();
match reader.read_to_string(&mut string).await {
Ok(_) => Success(string),
Err(e) => Failure((Status::BadRequest, e)),
}
})
let mut string = String::new();
let mut reader = data.open();
match reader.read_to_string(&mut string).await {
Ok(_) => Success(string),
Err(e) => Failure((Status::BadRequest, e)),
}
}
}
#[cfg(debug_assertions)]
impl FromDataSimple for Vec<u8> {
#[crate::async_trait]
impl FromData for Vec<u8> {
type Error = std::io::Error;
#[inline(always)]
fn from_data(_: &Request<'_>, data: Data) -> FromDataFuture<'static, Self, Self::Error> {
async fn from_data(_: &Request<'_>, data: Data) -> Outcome<Self, Self::Error> {
use tokio::io::AsyncReadExt;
Box::pin(async {
let mut stream = data.open();
let mut buf = Vec::new();
match stream.read_to_end(&mut buf).await {
Ok(_) => Success(buf),
Err(e) => Failure((Status::BadRequest, e)),
}
})
let mut stream = data.open();
let mut buf = Vec::new();
match stream.read_to_end(&mut buf).await {
Ok(_) => Success(buf),
Err(e) => Failure((Status::BadRequest, e)),
}
}
}

View File

@ -6,4 +6,4 @@ mod from_data;
pub use self::data::Data;
pub use self::data_stream::DataStream;
pub use self::from_data::{FromData, FromDataFuture, FromDataSimple, Outcome, Transform, Transformed, TransformFuture};
pub use self::from_data::{FromTransformedData, FromDataFuture, FromData, Outcome, Transform, Transformed, TransformFuture};

View File

@ -90,7 +90,7 @@ pub use self::info_kind::{Info, Kind};
///
/// [request guard]: crate::request::FromRequest
/// [request guards]: crate::request::FromRequest
/// [data guards]: crate::data::FromData
/// [data guards]: crate::data::FromTransformedData
///
/// ## Fairing Callbacks
///

View File

@ -9,11 +9,11 @@
//! processing next.
//!
//! The `Outcome` type is the return type of many of the core Rocket traits,
//! including [`FromRequest`](crate::request::FromRequest), [`FromData`]
//! including [`FromRequest`](crate::request::FromRequest), [`FromTransformedData`]
//! [`Responder`]. It is also the return type of request handlers via the
//! [`Response`](crate::response::Response) type.
//!
//! [`FromData`]: crate::data::FromData
//! [`FromTransformedData`]: crate::data::FromTransformedData
//! [`Responder`]: crate::response::Responder
//!
//! # Success
@ -21,7 +21,7 @@
//! A successful `Outcome<S, E, F>`, `Success(S)`, is returned from functions
//! that complete successfully. The meaning of a `Success` outcome depends on
//! the context. For instance, the `Outcome` of the `from_data` method of the
//! [`FromData`] trait will be matched against the type expected by the user.
//! [`FromTransformedData`] trait will be matched against the type expected by the user.
//! For example, consider the following handler:
//!
//! ```rust,ignore
@ -29,7 +29,7 @@
//! fn hello(my_val: S) -> ... { }
//! ```
//!
//! The [`FromData`] implementation for the type `S` returns an `Outcome` with a
//! The [`FromTransformedData`] implementation for the type `S` returns an `Outcome` with a
//! `Success(S)`. If `from_data` returns a `Success`, the `Success` value will
//! be unwrapped and the value will be used as the value of `my_val`.
//!
@ -50,7 +50,7 @@
//! fn hello(my_val: Result<S, E>) -> ... { }
//! ```
//!
//! The [`FromData`] implementation for the type `S` returns an `Outcome` with a
//! The [`FromTransformedData`] implementation for the type `S` returns an `Outcome` with a
//! `Success(S)` and `Failure(E)`. If `from_data` returns a `Failure`, the
//! `Failure` value will be unwrapped and the value will be used as the `Err`
//! value of `my_val` while a `Success` will be unwrapped and used the `Ok`
@ -71,7 +71,7 @@
//! fn hello(my_val: S) -> ... { }
//! ```
//!
//! The [`FromData`] implementation for the type `S` returns an `Outcome` with a
//! The [`FromTransformedData`] implementation for the type `S` returns an `Outcome` with a
//! `Success(S)`, `Failure(E)`, and `Forward(F)`. If the `Outcome` is a
//! `Forward`, the `hello` handler isn't called. Instead, the incoming request
//! is forwarded, or passed on to, the next matching route, if any. Ultimately,

View File

@ -22,7 +22,7 @@ pub enum FormParseError<'f> {
Missing(&'f RawStr),
}
/// Error returned by the [`FromData`](crate::data::FromData) implementations of
/// Error returned by the [`FromTransformedData`](crate::data::FromTransformedData) implementations of
/// [`Form`](crate::request::Form) and [`LenientForm`](crate::request::LenientForm).
#[derive(Debug)]
pub enum FormDataError<'f, E> {
@ -37,13 +37,13 @@ pub enum FormDataError<'f, E> {
Parse(E, &'f str)
}
/// Alias to the type of form errors returned by the [`FromData`]
/// Alias to the type of form errors returned by the [`FromTransformedData`]
/// implementations of [`Form<T>`] where the [`FromForm`] implementation for `T`
/// was derived.
///
/// This alias is particularly useful when "catching" form errors in routes.
///
/// [`FromData`]: crate::data::FromData
/// [`FromTransformedData`]: crate::data::FromTransformedData
/// [`Form<T>`]: crate::request::Form
/// [`FromForm`]: crate::request::FromForm
///

View File

@ -4,12 +4,12 @@ use tokio::io::AsyncReadExt;
use crate::outcome::Outcome::*;
use crate::request::{Request, form::{FromForm, FormItems, FormDataError}};
use crate::data::{Outcome, Transform, Transformed, Data, FromData, TransformFuture, FromDataFuture};
use crate::data::{Outcome, Transform, Transformed, Data, FromTransformedData, TransformFuture, FromDataFuture};
use crate::http::{Status, uri::{Query, FromUriParam}};
/// A data guard for parsing [`FromForm`] types strictly.
///
/// This type implements the [`FromData`] trait. It provides a generic means to
/// This type implements the [`FromTransformedData`] trait. It provides a generic means to
/// parse arbitrary structures from incoming form data.
///
/// # Strictness
@ -27,7 +27,7 @@ use crate::http::{Status, uri::{Query, FromUriParam}};
/// The trait can be automatically derived; see the [`FromForm`] documentation
/// for more information on deriving or implementing the trait.
///
/// Because `Form` implements `FromData`, it can be used directly as a target of
/// Because `Form` implements `FromTransformedData`, it can be used directly as a target of
/// the `data = "<param>"` route parameter as long as its generic type
/// implements the `FromForm` trait:
///
@ -183,7 +183,7 @@ impl<'f, T: FromForm<'f>> Form<T> {
///
/// All relevant warnings and errors are written to the console in Rocket
/// logging format.
impl<'f, T: FromForm<'f> + Send + 'f> FromData<'f> for Form<T> {
impl<'f, T: FromForm<'f> + Send + 'f> FromTransformedData<'f> for Form<T> {
type Error = FormDataError<'f, T::Error>;
type Owned = String;
type Borrowed = str;

View File

@ -1,12 +1,12 @@
use std::ops::Deref;
use crate::request::{Request, form::{Form, FormDataError, FromForm}};
use crate::data::{Data, Transformed, FromData, TransformFuture, FromDataFuture};
use crate::data::{Data, Transformed, FromTransformedData, TransformFuture, FromDataFuture};
use crate::http::uri::{Query, FromUriParam};
/// A data guard for parsing [`FromForm`] types leniently.
///
/// This type implements the [`FromData`] trait, and like [`Form`], provides a
/// This type implements the [`FromTransformedData`] trait, and like [`Form`], provides a
/// generic means to parse arbitrary structures from incoming form data. Unlike
/// `Form`, this type uses a _lenient_ parsing strategy: forms that contains a
/// superset of the expected fields (i.e, extra fields) will parse successfully.
@ -24,7 +24,7 @@ use crate::http::uri::{Query, FromUriParam};
/// The usage of a `LenientForm` type is equivalent to that of [`Form`], so we
/// defer details to its documentation.
///
/// `LenientForm` implements `FromData`, so it can be used directly as a target
/// `LenientForm` implements `FromTransformedData`, so it can be used directly as a target
/// of the `data = "<param>"` route parameter. For instance, if some structure
/// of type `T` implements the `FromForm` trait, an incoming form can be
/// automatically parsed into the `T` structure with the following route and
@ -93,7 +93,7 @@ impl<T> Deref for LenientForm<T> {
}
}
impl<'f, T: FromForm<'f> + Send + 'f> FromData<'f> for LenientForm<T> {
impl<'f, T: FromForm<'f> + Send + 'f> FromTransformedData<'f> for LenientForm<T> {
type Error = FormDataError<'f, T::Error>;
type Owned = String;
type Borrowed = str;

View File

@ -1,8 +1,8 @@
#[macro_use] extern crate rocket;
use rocket::Outcome::*;
use rocket::{Request, Data};
use rocket::request::{self, FromRequest};
use rocket::outcome::IntoOutcome;
struct HasContentType;
@ -10,26 +10,19 @@ struct HasContentType;
impl<'a, 'r> FromRequest<'a, 'r> for HasContentType {
type Error = ();
async fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, ()> {
if request.content_type().is_some() {
Success(HasContentType)
} else {
Forward(())
}
async fn from_request(req: &'a Request<'r>) -> request::Outcome<Self, ()> {
req.content_type().map(|_| HasContentType).or_forward(())
}
}
use rocket::data::{self, FromDataSimple};
use rocket::data::{self, FromData};
impl FromDataSimple for HasContentType {
#[rocket::async_trait]
impl FromData for HasContentType {
type Error = ();
fn from_data(request: &Request<'_>, data: Data) -> data::FromDataFuture<'static, Self, Self::Error> {
Box::pin(futures::future::ready(if request.content_type().is_some() {
Success(HasContentType)
} else {
Forward(data)
}))
async fn from_data(req: &Request<'_>, data: Data) -> data::Outcome<Self, ()> {
req.content_type().map(|_| HasContentType).or_forward(data)
}
}