mirror of https://github.com/rwf2/Rocket.git
Update docs, examples and tests
This commit is contained in:
parent
872595b8fa
commit
6b9fb1aa97
|
@ -1361,13 +1361,13 @@ pub fn catchers(input: TokenStream) -> TokenStream {
|
|||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate rocket;
|
||||
/// #[get("/person/<name>")]
|
||||
/// fn maybe(name: Option<&str>) { }
|
||||
/// // #[get("/person/<name>")]
|
||||
/// // fn maybe(name: Option<&str>) { }
|
||||
///
|
||||
/// let bob1 = uri!(maybe(name = "Bob"));
|
||||
/// let bob2 = uri!(maybe("Bob Smith"));
|
||||
/// assert_eq!(bob1.to_string(), "/person/Bob");
|
||||
/// assert_eq!(bob2.to_string(), "/person/Bob%20Smith");
|
||||
/// // let bob1 = uri!(maybe(name = "Bob"));
|
||||
/// // let bob2 = uri!(maybe("Bob Smith"));
|
||||
/// // assert_eq!(bob1.to_string(), "/person/Bob");
|
||||
/// // assert_eq!(bob2.to_string(), "/person/Bob%20Smith");
|
||||
///
|
||||
/// #[get("/person/<age>")]
|
||||
/// fn ok(age: Result<u8, std::num::ParseIntError>) { }
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
|
||||
#[macro_use] extern crate rocket;
|
||||
|
||||
use std::num::ParseIntError;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use rocket::error::Empty;
|
||||
use rocket::http::CookieJar;
|
||||
use rocket::http::uri::fmt::{FromUriParam, Query};
|
||||
use rocket::form::{Form, error::{Errors, ErrorKind}};
|
||||
|
@ -474,8 +476,8 @@ struct Third<'r> {
|
|||
|
||||
#[post("/<foo>/<bar>?<q1>&<rest..>")]
|
||||
fn optionals(
|
||||
foo: Option<usize>,
|
||||
bar: Option<String>,
|
||||
foo: Result<usize, ParseIntError>,
|
||||
bar: Result<String, Empty>,
|
||||
q1: Result<usize, Errors<'_>>,
|
||||
rest: Option<Third<'_>>
|
||||
) { }
|
||||
|
@ -547,6 +549,13 @@ fn test_optional_uri_parameters() {
|
|||
q1 = _,
|
||||
rest = _,
|
||||
)) => "/10/hi%20there",
|
||||
|
||||
// uri!(optionals(
|
||||
// foo = 10,
|
||||
// bar = Err(Empty),
|
||||
// q1 = _,
|
||||
// rest = _,
|
||||
// )) => "/10/hi%20there",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -128,15 +128,11 @@ pub type Outcome<'r, T, E = <T as FromData<'r>>::Error>
|
|||
///
|
||||
/// * `Option<T>`
|
||||
///
|
||||
/// Forwards to `T`'s `FromData` implementation, capturing the outcome.
|
||||
/// Forwards to `T`'s `FromData` implementation if there is data, capturing the outcome.
|
||||
/// If `T` returns an Error or Forward, the `Option` returns the same.
|
||||
///
|
||||
/// - **Fails:** Never.
|
||||
///
|
||||
/// - **Succeeds:** Always. If `T`'s `FromData` implementation succeeds, the
|
||||
/// parsed value is returned in `Some`. If its implementation forwards or
|
||||
/// fails, `None` is returned.
|
||||
///
|
||||
/// - **Forwards:** Never.
|
||||
/// - **None:** If the data stream is empty.
|
||||
/// - **Some:** If `T` succeeds to parse the incoming data.
|
||||
///
|
||||
/// * `Result<T, T::Error>`
|
||||
///
|
||||
|
@ -423,6 +419,6 @@ impl<'r, T: FromData<'r>> FromData<'r> for Option<T> {
|
|||
Outcome::Success(None)
|
||||
} else {
|
||||
T::from_data(req, data).await.map(Some)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -106,7 +106,7 @@ use crate::http::uncased::AsUncased;
|
|||
/// |------------------------|-------------|-------------------|--------|--------|----------------------------------------------------|
|
||||
/// | [`Strict<T>`] | **strict** | if `strict` `T` | if `T` | if `T` | `T: FromForm` |
|
||||
/// | [`Lenient<T>`] | **lenient** | if `lenient` `T` | if `T` | if `T` | `T: FromForm` |
|
||||
/// | `Option<T>` | **strict** | `None` | if `T` | if `T` | Infallible, `T: FromForm` |
|
||||
/// | `Option<T>` | **strict** | `None` | if `T` | if `T` | `T: FromForm` |
|
||||
/// | [`Result<T>`] | _inherit_ | `T::finalize()` | if `T` | if `T` | Infallible, `T: FromForm` |
|
||||
/// | `Vec<T>` | _inherit_ | `vec![]` | if `T` | if `T` | `T: FromForm` |
|
||||
/// | [`HashMap<K, V>`] | _inherit_ | `HashMap::new()` | if `V` | if `V` | `K: FromForm + Eq + Hash`, `V: FromForm` |
|
||||
|
@ -151,6 +151,12 @@ use crate::http::uncased::AsUncased;
|
|||
///
|
||||
/// ## Additional Notes
|
||||
///
|
||||
/// * **`Option<T>` where `T: FromForm`**
|
||||
///
|
||||
/// `Option` is no longer Infallible. It now checks (depending on the strictness)
|
||||
/// for whether any values were passed in, and returns an error if any values were passed in,
|
||||
/// and the inner `T` failed to parse.
|
||||
///
|
||||
/// * **`Vec<T>` where `T: FromForm`**
|
||||
///
|
||||
/// Parses a sequence of `T`'s. A new `T` is created whenever the field
|
||||
|
@ -814,7 +820,7 @@ impl<'v, K, V> FromForm<'v> for BTreeMap<K, V>
|
|||
}
|
||||
}
|
||||
|
||||
struct OptionFormCtx<'v, T: FromForm<'v>> {
|
||||
pub struct OptionFormCtx<'v, T: FromForm<'v>> {
|
||||
strict: bool,
|
||||
has_field: bool,
|
||||
inner: T::Context,
|
||||
|
|
|
@ -105,18 +105,20 @@ fn defaults() {
|
|||
&[] => Result<'_, bool> = Ok(false),
|
||||
&[] => Result<'_, Strict<bool>> = Err(error::ErrorKind::Missing.into()),
|
||||
|
||||
&["=unknown"] => Option<bool> = None,
|
||||
&["=unknown"] => Option<Strict<bool>> = None,
|
||||
&["=unknown"] => Option<Lenient<bool>> = None,
|
||||
|
||||
&[] => Option<Lenient<bool>> = Some(false.into()),
|
||||
&["=123"] => Option<time::Date> = None,
|
||||
&[] => Option<Lenient<bool>> = None,
|
||||
|
||||
&["=no"] => Option<bool> = Some(false),
|
||||
&["=yes"] => Option<bool> = Some(true),
|
||||
&["=yes"] => Option<Lenient<bool>> = Some(true.into()),
|
||||
&["=yes"] => Option<Strict<bool>> = Some(true.into()),
|
||||
}
|
||||
|
||||
assert_parses_fail! {
|
||||
&["=unknown"] => Option<bool>,
|
||||
&["=unknown"] => Option<Strict<bool>>,
|
||||
&["=unknown"] => Option<Lenient<bool>>,
|
||||
&["=123"] => Option<time::Date>,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
//! ```rust
|
||||
//! # use rocket::post;
|
||||
//! # type S = Option<String>;
|
||||
//! # type E = std::convert::Infallible;
|
||||
//! # type E = std::io::Error;
|
||||
//! #[post("/", data = "<my_val>")]
|
||||
//! fn hello(my_val: Result<S, E>) { /* ... */ }
|
||||
//! ```
|
||||
|
|
|
@ -46,16 +46,9 @@ use crate::http::uri::{Segments, error::PathError, fmt::Path};
|
|||
///
|
||||
/// Sometimes, a forward is not desired, and instead, we simply want to know
|
||||
/// that the dynamic path segment could not be parsed into some desired type
|
||||
/// `T`. In these cases, types of `Option<T>`, `Result<T, T::Error>`, or
|
||||
/// `T`. In these cases, types of `Result<T, T::Error>`, or
|
||||
/// `Either<A, B>` can be used, which implement `FromParam` themselves.
|
||||
///
|
||||
/// * **`Option<T>`** _where_ **`T: FromParam`**
|
||||
///
|
||||
/// Always returns successfully.
|
||||
///
|
||||
/// If the conversion to `T` fails, `None` is returned. If the conversion
|
||||
/// succeeds, `Some(value)` is returned.
|
||||
///
|
||||
/// * **`Result<T, T::Error>`** _where_ **`T: FromParam`**
|
||||
///
|
||||
/// Always returns successfully.
|
||||
|
@ -121,14 +114,6 @@ use crate::http::uri::{Segments, error::PathError, fmt::Path};
|
|||
/// Returns the percent-decoded path segment with invalid UTF-8 byte
|
||||
/// sequences replaced by <20> U+FFFD.
|
||||
///
|
||||
/// * **Option<T>** _where_ **T: FromParam**
|
||||
///
|
||||
/// _This implementation always returns successfully._
|
||||
///
|
||||
/// The path segment is parsed by `T`'s `FromParam` implementation. If the
|
||||
/// parse succeeds, a `Some(parsed_value)` is returned. Otherwise, a `None`
|
||||
/// is returned.
|
||||
///
|
||||
/// * **Result<T, T::Error>** _where_ **T: FromParam**
|
||||
///
|
||||
/// _This implementation always returns successfully._
|
||||
|
@ -329,6 +314,11 @@ impl<'a, T: FromParam<'a>> FromParam<'a> for Result<T, T::Error> {
|
|||
/// any other segments that begin with "*" or "." are ignored. If a
|
||||
/// percent-decoded segment results in invalid UTF8, an `Err` is returned with
|
||||
/// the `Utf8Error`.
|
||||
///
|
||||
/// **`Option<T>` where `T: FromSegments`**
|
||||
///
|
||||
/// Succeeds as `None` if the this segments matched nothing, otherwise invokes
|
||||
/// `T`'s `FromSegments` implementation.
|
||||
pub trait FromSegments<'r>: Sized {
|
||||
/// The associated error to be returned when parsing fails.
|
||||
type Error: std::fmt::Debug;
|
||||
|
|
|
@ -84,8 +84,7 @@ pub type Outcome<S, E> = outcome::Outcome<S, (Status, E), Status>;
|
|||
/// If the `Outcome` is [`Error`], the request will fail with the given
|
||||
/// status code and error. The designated error [`Catcher`](crate::Catcher)
|
||||
/// will be used to respond to the request. Note that users can request types
|
||||
/// of `Result<S, E>` and `Option<S>` to catch `Error`s and retrieve the
|
||||
/// error value.
|
||||
/// of `Result<S, E>` to catch `Error`s and retrieve the error value.
|
||||
///
|
||||
/// * **Forward**(Status)
|
||||
///
|
||||
|
@ -177,9 +176,9 @@ pub type Outcome<S, E> = outcome::Outcome<S, (Status, E), Status>;
|
|||
///
|
||||
/// The type `T` is derived from the incoming request using `T`'s
|
||||
/// `FromRequest` implementation. If the derivation is a `Success`, the
|
||||
/// derived value is returned in `Some`. Otherwise, a `None` is returned.
|
||||
///
|
||||
/// _This implementation always returns successfully._
|
||||
/// derived value is returned in `Some`. If the derivation is a `Forward`,
|
||||
/// the result is `None`, and if the derivation is an `Error`, the `Error`
|
||||
/// is preserved.
|
||||
///
|
||||
/// * **Result<T, T::Error>** _where_ **T: FromRequest**
|
||||
///
|
||||
|
@ -189,6 +188,14 @@ pub type Outcome<S, E> = outcome::Outcome<S, (Status, E), Status>;
|
|||
/// returned in `Err`. If the derivation is a `Forward`, the request is
|
||||
/// forwarded with the same status code as the original forward.
|
||||
///
|
||||
/// * **Outcome<T, T::Error>** _where_ **T: FromRequest**
|
||||
///
|
||||
/// The type `T` is derived from the incoming request using `T`'s
|
||||
/// `FromRequest` implementation. The `Outcome` is then provided to the handler,
|
||||
/// reguardless of what it returned.
|
||||
///
|
||||
/// _This guard **always** succeeds_
|
||||
///
|
||||
/// [`Config`]: crate::config::Config
|
||||
///
|
||||
/// # Example
|
||||
|
@ -521,12 +528,13 @@ impl<'r, T: FromRequest<'r>> FromRequest<'r> for Result<T, T::Error> {
|
|||
|
||||
#[crate::async_trait]
|
||||
impl<'r, T: FromRequest<'r>> FromRequest<'r> for Option<T> {
|
||||
type Error = Infallible;
|
||||
type Error = T::Error;
|
||||
|
||||
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Infallible> {
|
||||
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
||||
match T::from_request(request).await {
|
||||
Success(val) => Success(Some(val)),
|
||||
Error(_) | Forward(_) => Success(None),
|
||||
Forward(_) => Success(None),
|
||||
Error(e) => Error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,13 +11,13 @@ fn set() -> Flash<&'static str> {
|
|||
}
|
||||
|
||||
#[get("/unused")]
|
||||
fn unused(flash: Option<FlashMessage<'_>>) -> Option<()> {
|
||||
flash.map(|_| ())
|
||||
fn unused(flash: Result<FlashMessage<'_>, ()>) -> Option<()> {
|
||||
flash.ok().map(|_| ())
|
||||
}
|
||||
|
||||
#[get("/use")]
|
||||
fn used(flash: Option<FlashMessage<'_>>) -> Option<String> {
|
||||
flash.map(|f| f.message().into())
|
||||
fn used(flash: Result<FlashMessage<'_>, ()>) -> Option<String> {
|
||||
flash.ok().map(|f| f.message().into())
|
||||
}
|
||||
|
||||
mod flash_lazy_remove_tests {
|
||||
|
|
|
@ -270,7 +270,7 @@ will be routed as follows:
|
|||
You'll also find a route's rank logged in brackets during application launch:
|
||||
`GET /user/<id> [3] (user_str)`.
|
||||
|
||||
Forwards can be _caught_ by using a `Result` or `Option` type. For example, if
|
||||
Forwards can be _caught_ by using a `Option` type. For example, if
|
||||
the type of `id` in the `user` function was `Result<usize, &str>`, then `user`
|
||||
would never forward. An `Ok` variant would indicate that `<id>` was a valid
|
||||
`usize`, while an `Err` would indicate that `<id>` was not a `usize`. The
|
||||
|
@ -279,7 +279,7 @@ would never forward. An `Ok` variant would indicate that `<id>` was a valid
|
|||
! tip: It's not just forwards that can be caught!
|
||||
|
||||
In general, when any guard fails for any reason, including parameter guards,
|
||||
you can use an `Option` or `Result` type in its place to catch the error.
|
||||
you can use a `Result` type in its place to catch the error.
|
||||
|
||||
By the way, if you were to omit the `rank` parameter in the `user_str` or
|
||||
`user_int` routes, Rocket would emit an error and abort launch, indicating that
|
||||
|
@ -511,8 +511,7 @@ it always succeeds. The user is redirected to a log in page.
|
|||
### Fallible Guards
|
||||
|
||||
A failing or forwarding guard can be "caught" in handler, preventing it from
|
||||
failing or forwarding, via the `Option<T>` and `Result<T, E>` guards. When a
|
||||
guard `T` fails or forwards, `Option<T>` will be `None`. If a guard `T` fails
|
||||
failing or forwarding, via `Result<T, E>` guards. If a guard `T` fails
|
||||
with error `E`, `Result<T, E>` will be `Err(E)`.
|
||||
|
||||
As an example, for the `User` guard above, instead of allowing the guard to
|
||||
|
@ -538,7 +537,7 @@ fn admin_panel_user(user: Option<User>) -> Result<&'static str, Redirect> {
|
|||
}
|
||||
```
|
||||
|
||||
If the `User` guard forwards or fails, the `Option` will be `None`. If it
|
||||
If the `User` guard forwards, the `Option` will be `None`. If it
|
||||
succeeds, it will be `Some(User)`.
|
||||
|
||||
For guards that may fail (and not just forward), the `Result<T, E>` guard allows
|
||||
|
@ -898,14 +897,14 @@ If a `POST /todo` request arrives, the form data will automatically be parsed
|
|||
into the `Task` structure. If the data that arrives isn't of the correct
|
||||
Content-Type, the request is forwarded. If the data doesn't parse or is simply
|
||||
invalid, a customizable error is returned. As before, a forward or error can
|
||||
be caught by using the `Option` and `Result` types:
|
||||
be caught by using the `Result` types:
|
||||
|
||||
```rust
|
||||
# use rocket::{post, form::Form};
|
||||
# use rocket::{post, form::{Form, Errors}};
|
||||
# type Task<'r> = &'r str;
|
||||
|
||||
#[post("/todo", data = "<task>")]
|
||||
fn new(task: Option<Form<Task<'_>>>) { /* .. */ }
|
||||
fn new(task: Result<Form<Task<'_>>, Errors<'_>>) { /* .. */ }
|
||||
```
|
||||
|
||||
### Multipart
|
||||
|
|
|
@ -616,18 +616,20 @@ For example, given the following route:
|
|||
```rust
|
||||
# #[macro_use] extern crate rocket;
|
||||
# fn main() {}
|
||||
# use std::num::ParseIntError;
|
||||
|
||||
#[get("/<id>/<name>?<age>")]
|
||||
fn person(id: Option<usize>, name: &str, age: Option<u8>) { /* .. */ }
|
||||
fn person(id: Result<usize, ParseIntError>, name: &str, age: Option<u8>) { /* .. */ }
|
||||
```
|
||||
|
||||
URIs to `person` can be created as follows:
|
||||
|
||||
```rust
|
||||
# #[macro_use] extern crate rocket;
|
||||
# use std::num::ParseIntError;
|
||||
|
||||
# #[get("/<id>/<name>?<age>")]
|
||||
# fn person(id: Option<usize>, name: &str, age: Option<u8>) { /* .. */ }
|
||||
# fn person(id: Result<usize, ParseIntError>, name: &str, age: Option<u8>) { /* .. */ }
|
||||
|
||||
// with unnamed parameters, in route path declaration order
|
||||
let mike = uri!(person(101, "Mike Smith", Some(28)));
|
||||
|
@ -755,9 +757,10 @@ generated.
|
|||
|
||||
```rust
|
||||
# #[macro_use] extern crate rocket;
|
||||
# use std::num::ParseIntError;
|
||||
|
||||
#[get("/<id>/<name>?<age>")]
|
||||
fn person(id: Option<usize>, name: &str, age: Option<u8>) { /* .. */ }
|
||||
fn person(id: Result<usize, ParseIntError>, name: &str, age: Option<u8>) { /* .. */ }
|
||||
|
||||
// Note that `id` is `Option<usize>` in the route, but `id` in `uri!` _cannot_
|
||||
// be an `Option`. `age`, on the other hand, _must_ be an `Option` (or `Result`
|
||||
|
|
|
@ -53,8 +53,8 @@ fn login(_user: User) -> Redirect {
|
|||
}
|
||||
|
||||
#[get("/login", rank = 2)]
|
||||
fn login_page(flash: Option<FlashMessage<'_>>) -> Template {
|
||||
Template::render("login", flash)
|
||||
fn login_page(flash: Result<FlashMessage<'_>, ()>) -> Template {
|
||||
Template::render("login", flash.ok())
|
||||
}
|
||||
|
||||
#[post("/login", data = "<login>")]
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#[macro_use] extern crate rocket;
|
||||
|
||||
use rocket::form::Errors;
|
||||
|
||||
#[cfg(test)] mod tests;
|
||||
|
||||
#[derive(FromFormField)]
|
||||
|
@ -51,13 +53,13 @@ fn wave(name: &str, age: u8) -> String {
|
|||
// http://127.0.0.1:8000/?name=Rocketeer&lang=en&emoji
|
||||
// http://127.0.0.1:8000/?lang=ru&emoji&name=Rocketeer
|
||||
#[get("/?<lang>&<opt..>")]
|
||||
fn hello(lang: Option<Lang>, opt: Options<'_>) -> String {
|
||||
fn hello(lang: Result<Lang, Errors<'_>>, opt: Options<'_>) -> String {
|
||||
let mut greeting = String::new();
|
||||
if opt.emoji {
|
||||
greeting.push_str("👋 ");
|
||||
}
|
||||
|
||||
match lang {
|
||||
match lang.ok() {
|
||||
Some(Lang::Russian) => greeting.push_str("Привет"),
|
||||
Some(Lang::English) => greeting.push_str("Hello"),
|
||||
None => greeting.push_str("Hi"),
|
||||
|
|
|
@ -86,8 +86,8 @@ async fn delete(id: i32, conn: DbConn) -> Result<Flash<Redirect>, Template> {
|
|||
}
|
||||
|
||||
#[get("/")]
|
||||
async fn index(flash: Option<FlashMessage<'_>>, conn: DbConn) -> Template {
|
||||
let flash = flash.map(FlashMessage::into_inner);
|
||||
async fn index(flash: Result<FlashMessage<'_>, ()>, conn: DbConn) -> Template {
|
||||
let flash = flash.ok().map(FlashMessage::into_inner);
|
||||
Template::render("index", Context::raw(&conn, flash).await)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue