From 33e95f49008dcbc8dc51da7d37e0570059176b73 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Sun, 12 Jul 2020 02:23:00 -0700 Subject: [PATCH] Rename 'FromDataSimple' to 'FromData'. Make async. The 'FromData' trait becomes 'FromTransformedData'. --- contrib/lib/src/json.rs | 6 +- contrib/lib/src/msgpack.rs | 6 +- core/codegen/src/attribute/route.rs | 6 +- core/codegen/src/lib.rs | 10 +- core/codegen/tests/route-data.rs | 21 +- core/codegen/tests/route.rs | 17 +- .../tests/ui-fail/route-type-errors.stderr | 6 +- core/lib/src/data/data.rs | 6 +- core/lib/src/data/from_data.rs | 231 ++++++++++-------- core/lib/src/data/mod.rs | 2 +- core/lib/src/fairing/mod.rs | 2 +- core/lib/src/outcome.rs | 12 +- core/lib/src/request/form/error.rs | 6 +- core/lib/src/request/form/form.rs | 8 +- core/lib/src/request/form/lenient.rs | 8 +- .../local-request-content-type-issue-505.rs | 23 +- 16 files changed, 191 insertions(+), 179 deletions(-) diff --git a/contrib/lib/src/json.rs b/contrib/lib/src/json.rs index 029abd1e..06e7a1e8 100644 --- a/contrib/lib/src/json.rs +++ b/contrib/lib/src/json.rs @@ -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 { +impl<'a, T: Deserialize<'a>> FromTransformedData<'a> for Json { type Error = JsonError<'a>; type Owned = String; type Borrowed = str; diff --git a/contrib/lib/src/msgpack.rs b/contrib/lib/src/msgpack.rs index 5602b98d..f2434e55 100644 --- a/contrib/lib/src/msgpack.rs +++ b/contrib/lib/src/msgpack.rs @@ -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 MsgPack { /// Default limit for MessagePack is 1MB. const LIMIT: u64 = 1 << 20; -impl<'a, T: Deserialize<'a>> FromData<'a> for MsgPack { +impl<'a, T: Deserialize<'a>> FromTransformedData<'a> for MsgPack { type Error = Error; type Owned = Vec; type Borrowed = [u8]; diff --git a/core/codegen/src/attribute/route.rs b/core/codegen/src/attribute/route.rs index 879fa298..9a0b316b 100644 --- a/core/codegen/src/attribute/route.rs +++ b/core/codegen/src/attribute/route.rs @@ -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), diff --git a/core/codegen/src/lib.rs b/core/codegen/src/lib.rs index e4742766..39d8f151 100644 --- a/core/codegen/src/lib.rs +++ b/core/codegen/src/lib.rs @@ -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 | `` | [`FromSegments`] | /// | query | `` | [`FromFormValue`] | /// | query | `` | [`FromQuery`] | - /// | data | `` | [`FromData`] | + /// | data | `` | [`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)) diff --git a/core/codegen/tests/route-data.rs b/core/codegen/tests/route-data.rs index 29c2fafd..07062e66 100644 --- a/core/codegen/tests/route-data.rs +++ b/core/codegen/tests/route-data.rs @@ -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 { + 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)) } } diff --git a/core/codegen/tests/route.rs b/core/codegen/tests/route.rs index 67ad6603..ff0aeeaf 100644 --- a/core/codegen/tests/route.rs +++ b/core/codegen/tests/route.rs @@ -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 { + let mut string = String::new(); + let mut stream = data.open().take(64); + stream.read_to_string(&mut string).await.unwrap(); + Success(Simple(string)) } } diff --git a/core/codegen/tests/ui-fail/route-type-errors.stderr b/core/codegen/tests/ui-fail/route-type-errors.stderr index 1fada777..58e360a5 100644 --- a/core/codegen/tests/ui-fail/route-type-errors.stderr +++ b/core/codegen/tests/ui-fail/route-type-errors.stderr @@ -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 diff --git a/core/lib/src/data/data.rs b/core/lib/src/data/data.rs index 04f32f51..b4126889 100644 --- a/core/lib/src/data/data.rs +++ b/core/lib/src/data/data.rs @@ -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 = ""` 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 /// diff --git a/core/lib/src/data/from_data.rs b/core/lib/src/data/from_data.rs index 1ecf0010..e835bfa0 100644 --- a/core/lib/src/data/from_data.rs +++ b/core/lib/src/data/from_data.rs @@ -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 = outcome::Outcome; impl IntoOutcome for Result { @@ -36,14 +36,14 @@ impl IntoOutcome for Result { /// 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 { /// 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 Transform { } } -/// 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<>::Owned, >::Error>, - Outcome<&'a >::Borrowed, >::Error> + Outcome<>::Owned, >::Error>, + Outcome<&'a >::Borrowed, >::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>>; -/// 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>; /// 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>; /// /// 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>; /// /// 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>; /// # 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>; /// 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>; /// 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>; /// /// # 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>; /// /// _This implementation always returns successfully._ /// -/// * **Option<T>** _where_ **T: FromData** +/// * **Option<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<T, T::Error>** _where_ **T: FromData** +/// * **Result<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>; /// /// * **Vec<u8>** /// -/// **Note:** _An implementation of `FromData` for `Vec` is only +/// **Note:** _An implementation of `FromTransformedData` for `Vec` is only /// available when compiling in debug mode!_ /// /// Reads the entire request body into a `Vec`. If reading fails, @@ -329,24 +329,24 @@ pub type FromDataFuture<'fut, T, E> = BoxFuture<'fut, Outcome>; /// 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; - /// 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>::Error> { + /// # use rocket::data::{Data, FromTransformedData, Transformed, Outcome}; + /// # fn f<'a>(outcome: Transformed<'a, Data>) -> Outcome>::Error> { /// // If `Owned` was returned from `transform`: /// let data = try_outcome!(outcome.owned()); /// # unimplemented!() /// # } /// - /// # fn g<'a>(outcome: Transformed<'a, Data>) -> Outcome>::Error> { + /// # fn g<'a>(outcome: Transformed<'a, Data>) -> Outcome>::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 { +/// /* .. */ +/// # 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 `:` 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 { /// // 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 = "")] @@ -510,7 +530,8 @@ impl<'a> FromData<'a> for Data { /// # fn person2(person: Result) { } /// # 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; } -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 { +impl<'a, T: FromTransformedData<'a> + 'a> FromTransformedData<'a> for Result { 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 { } } -impl<'a, T: FromData<'a> + 'a> FromData<'a> for Option { +impl<'a, T: FromTransformedData<'a> + 'a> FromTransformedData<'a> for Option { 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 { } #[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 { 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 { +#[crate::async_trait] +impl FromData for Vec { 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 { 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)), + } } } diff --git a/core/lib/src/data/mod.rs b/core/lib/src/data/mod.rs index 350b2685..f127efe9 100644 --- a/core/lib/src/data/mod.rs +++ b/core/lib/src/data/mod.rs @@ -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}; diff --git a/core/lib/src/fairing/mod.rs b/core/lib/src/fairing/mod.rs index f2062a60..ce545368 100644 --- a/core/lib/src/fairing/mod.rs +++ b/core/lib/src/fairing/mod.rs @@ -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 /// diff --git a/core/lib/src/outcome.rs b/core/lib/src/outcome.rs index 80452f62..d6224ae2 100644 --- a/core/lib/src/outcome.rs +++ b/core/lib/src/outcome.rs @@ -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`, `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) -> ... { } //! ``` //! -//! 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, diff --git a/core/lib/src/request/form/error.rs b/core/lib/src/request/form/error.rs index 97458503..a707b735 100644 --- a/core/lib/src/request/form/error.rs +++ b/core/lib/src/request/form/error.rs @@ -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`] 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`]: crate::request::Form /// [`FromForm`]: crate::request::FromForm /// diff --git a/core/lib/src/request/form/form.rs b/core/lib/src/request/form/form.rs index 02e07b6b..a8351d38 100644 --- a/core/lib/src/request/form/form.rs +++ b/core/lib/src/request/form/form.rs @@ -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 = ""` route parameter as long as its generic type /// implements the `FromForm` trait: /// @@ -183,7 +183,7 @@ impl<'f, T: FromForm<'f>> Form { /// /// 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 { +impl<'f, T: FromForm<'f> + Send + 'f> FromTransformedData<'f> for Form { type Error = FormDataError<'f, T::Error>; type Owned = String; type Borrowed = str; diff --git a/core/lib/src/request/form/lenient.rs b/core/lib/src/request/form/lenient.rs index 87d76052..c88abff5 100644 --- a/core/lib/src/request/form/lenient.rs +++ b/core/lib/src/request/form/lenient.rs @@ -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 = ""` 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 Deref for LenientForm { } } -impl<'f, T: FromForm<'f> + Send + 'f> FromData<'f> for LenientForm { +impl<'f, T: FromForm<'f> + Send + 'f> FromTransformedData<'f> for LenientForm { type Error = FormDataError<'f, T::Error>; type Owned = String; type Borrowed = str; diff --git a/core/lib/tests/local-request-content-type-issue-505.rs b/core/lib/tests/local-request-content-type-issue-505.rs index 4ef1b66a..fafb3053 100644 --- a/core/lib/tests/local-request-content-type-issue-505.rs +++ b/core/lib/tests/local-request-content-type-issue-505.rs @@ -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 { - if request.content_type().is_some() { - Success(HasContentType) - } else { - Forward(()) - } + async fn from_request(req: &'a Request<'r>) -> request::Outcome { + 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 { + req.content_type().map(|_| HasContentType).or_forward(data) } }