Skip to content

Commit 913efc4

Browse files
committed
Update docs, examples and tests
1 parent fd7e000 commit 913efc4

File tree

14 files changed

+86
-71
lines changed

14 files changed

+86
-71
lines changed

core/codegen/src/lib.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -1319,13 +1319,13 @@ pub fn catchers(input: TokenStream) -> TokenStream {
13191319
///
13201320
/// ```rust
13211321
/// # #[macro_use] extern crate rocket;
1322-
/// #[get("/person/<name>")]
1323-
/// fn maybe(name: Option<&str>) { }
1322+
/// // #[get("/person/<name>")]
1323+
/// // fn maybe(name: Option<&str>) { }
13241324
///
1325-
/// let bob1 = uri!(maybe(name = "Bob"));
1326-
/// let bob2 = uri!(maybe("Bob Smith"));
1327-
/// assert_eq!(bob1.to_string(), "/person/Bob");
1328-
/// assert_eq!(bob2.to_string(), "/person/Bob%20Smith");
1325+
/// // let bob1 = uri!(maybe(name = "Bob"));
1326+
/// // let bob2 = uri!(maybe("Bob Smith"));
1327+
/// // assert_eq!(bob1.to_string(), "/person/Bob");
1328+
/// // assert_eq!(bob2.to_string(), "/person/Bob%20Smith");
13291329
///
13301330
/// #[get("/person/<age>")]
13311331
/// fn ok(age: Result<u8, std::num::ParseIntError>) { }

core/codegen/tests/typed-uris.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
#[macro_use] extern crate rocket;
44

5+
use std::num::ParseIntError;
56
use std::path::PathBuf;
67

8+
use rocket::error::Empty;
79
use rocket::http::CookieJar;
810
use rocket::http::uri::fmt::{FromUriParam, Query};
911
use rocket::form::{Form, error::{Errors, ErrorKind}};
@@ -474,8 +476,8 @@ struct Third<'r> {
474476

475477
#[post("/<foo>/<bar>?<q1>&<rest..>")]
476478
fn optionals(
477-
foo: Option<usize>,
478-
bar: Option<String>,
479+
foo: Result<usize, ParseIntError>,
480+
bar: Result<String, Empty>,
479481
q1: Result<usize, Errors<'_>>,
480482
rest: Option<Third<'_>>
481483
) { }
@@ -547,6 +549,13 @@ fn test_optional_uri_parameters() {
547549
q1 = _,
548550
rest = _,
549551
)) => "/10/hi%20there",
552+
553+
// uri!(optionals(
554+
// foo = 10,
555+
// bar = Err(Empty),
556+
// q1 = _,
557+
// rest = _,
558+
// )) => "/10/hi%20there",
550559
}
551560
}
552561

core/lib/src/data/from_data.rs

+5-9
Original file line numberDiff line numberDiff line change
@@ -128,15 +128,11 @@ pub type Outcome<'r, T, E = <T as FromData<'r>>::Error>
128128
///
129129
/// * `Option<T>`
130130
///
131-
/// Forwards to `T`'s `FromData` implementation, capturing the outcome.
132-
///
133-
/// - **Fails:** Never.
131+
/// Forwards to `T`'s `FromData` implementation if there is data, capturing the outcome.
132+
/// If `T` returns an Error or Forward, the `Option` returns the same.
134133
///
135-
/// - **Succeeds:** Always. If `T`'s `FromData` implementation succeeds, the
136-
/// parsed value is returned in `Some`. If its implementation forwards or
137-
/// fails, `None` is returned.
138-
///
139-
/// - **Forwards:** Never.
134+
/// - **None:** If the data stream is empty.
135+
/// - **Some:** If `T` succeeds to parse the incoming data.
140136
///
141137
/// * `Result<T, T::Error>`
142138
///
@@ -423,6 +419,6 @@ impl<'r, T: FromData<'r>> FromData<'r> for Option<T> {
423419
Outcome::Success(None)
424420
} else {
425421
T::from_data(req, data).await.map(Some)
426-
}
422+
}
427423
}
428424
}

core/lib/src/form/from_form.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ use crate::http::uncased::AsUncased;
106106
/// |------------------------|-------------|-------------------|--------|--------|----------------------------------------------------|
107107
/// | [`Strict<T>`] | **strict** | if `strict` `T` | if `T` | if `T` | `T: FromForm` |
108108
/// | [`Lenient<T>`] | **lenient** | if `lenient` `T` | if `T` | if `T` | `T: FromForm` |
109-
/// | `Option<T>` | **strict** | `None` | if `T` | if `T` | Infallible, `T: FromForm` |
109+
/// | `Option<T>` | **strict** | `None` | if `T` | if `T` | `T: FromForm` |
110110
/// | [`Result<T>`] | _inherit_ | `T::finalize()` | if `T` | if `T` | Infallible, `T: FromForm` |
111111
/// | `Vec<T>` | _inherit_ | `vec![]` | if `T` | if `T` | `T: FromForm` |
112112
/// | [`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;
151151
///
152152
/// ## Additional Notes
153153
///
154+
/// * **`Option<T>` where `T: FromForm`**
155+
///
156+
/// `Option` is no longer Infallible. It now checks (depending on the strictness)
157+
/// for whether any values were passed in, and returns an error if any values were passed in,
158+
/// and the inner `T` failed to parse.
159+
///
154160
/// * **`Vec<T>` where `T: FromForm`**
155161
///
156162
/// 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>
814820
}
815821
}
816822

817-
struct OptionFormCtx<'v, T: FromForm<'v>> {
823+
pub struct OptionFormCtx<'v, T: FromForm<'v>> {
818824
strict: bool,
819825
has_field: bool,
820826
inner: T::Context,

core/lib/src/form/tests.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -105,18 +105,20 @@ fn defaults() {
105105
&[] => Result<'_, bool> = Ok(false),
106106
&[] => Result<'_, Strict<bool>> = Err(error::ErrorKind::Missing.into()),
107107

108-
&["=unknown"] => Option<bool> = None,
109-
&["=unknown"] => Option<Strict<bool>> = None,
110-
&["=unknown"] => Option<Lenient<bool>> = None,
111-
112-
&[] => Option<Lenient<bool>> = Some(false.into()),
113-
&["=123"] => Option<time::Date> = None,
108+
&[] => Option<Lenient<bool>> = None,
114109

115110
&["=no"] => Option<bool> = Some(false),
116111
&["=yes"] => Option<bool> = Some(true),
117112
&["=yes"] => Option<Lenient<bool>> = Some(true.into()),
118113
&["=yes"] => Option<Strict<bool>> = Some(true.into()),
119114
}
115+
116+
assert_parses_fail! {
117+
&["=unknown"] => Option<bool>,
118+
&["=unknown"] => Option<Strict<bool>>,
119+
&["=unknown"] => Option<Lenient<bool>>,
120+
&["=123"] => Option<time::Date>,
121+
}
120122
}
121123

122124
#[test]

core/lib/src/outcome.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
//! ```rust
5151
//! # use rocket::post;
5252
//! # type S = Option<String>;
53-
//! # type E = std::convert::Infallible;
53+
//! # type E = std::io::Error;
5454
//! #[post("/", data = "<my_val>")]
5555
//! fn hello(my_val: Result<S, E>) { /* ... */ }
5656
//! ```

core/lib/src/request/from_param.rs

+6-16
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,9 @@ use crate::http::uri::{Segments, error::PathError, fmt::Path};
4141
///
4242
/// Sometimes, a forward is not desired, and instead, we simply want to know
4343
/// that the dynamic path segment could not be parsed into some desired type
44-
/// `T`. In these cases, types of `Option<T>`, `Result<T, T::Error>`, or
44+
/// `T`. In these cases, types of `Result<T, T::Error>`, or
4545
/// `Either<A, B>` can be used, which implement `FromParam` themselves.
4646
///
47-
/// * **`Option<T>`** _where_ **`T: FromParam`**
48-
///
49-
/// Always returns successfully.
50-
///
51-
/// If the conversion to `T` fails, `None` is returned. If the conversion
52-
/// succeeds, `Some(value)` is returned.
53-
///
5447
/// * **`Result<T, T::Error>`** _where_ **`T: FromParam`**
5548
///
5649
/// Always returns successfully.
@@ -116,14 +109,6 @@ use crate::http::uri::{Segments, error::PathError, fmt::Path};
116109
/// Returns the percent-decoded path segment with invalid UTF-8 byte
117110
/// sequences replaced by � U+FFFD.
118111
///
119-
/// * **Option&lt;T>** _where_ **T: FromParam**
120-
///
121-
/// _This implementation always returns successfully._
122-
///
123-
/// The path segment is parsed by `T`'s `FromParam` implementation. If the
124-
/// parse succeeds, a `Some(parsed_value)` is returned. Otherwise, a `None`
125-
/// is returned.
126-
///
127112
/// * **Result&lt;T, T::Error>** _where_ **T: FromParam**
128113
///
129114
/// _This implementation always returns successfully._
@@ -324,6 +309,11 @@ impl<'a, T: FromParam<'a>> FromParam<'a> for Result<T, T::Error> {
324309
/// any other segments that begin with "*" or "." are ignored. If a
325310
/// percent-decoded segment results in invalid UTF8, an `Err` is returned with
326311
/// the `Utf8Error`.
312+
///
313+
/// **`Option&lt;T>` where `T: FromSegments`**
314+
///
315+
/// Succeeds as `None` if the this segments matched nothing, otherwise invokes
316+
/// `T`'s `FromSegments` implementation.
327317
pub trait FromSegments<'r>: Sized {
328318
/// The associated error to be returned when parsing fails.
329319
type Error: std::fmt::Debug;

core/lib/src/request/from_request.rs

+16-8
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,7 @@ pub type Outcome<S, E> = outcome::Outcome<S, (Status, E), Status>;
8484
/// If the `Outcome` is [`Error`], the request will fail with the given
8585
/// status code and error. The designated error [`Catcher`](crate::Catcher)
8686
/// will be used to respond to the request. Note that users can request types
87-
/// of `Result<S, E>` and `Option<S>` to catch `Error`s and retrieve the
88-
/// error value.
87+
/// of `Result<S, E>` to catch `Error`s and retrieve the error value.
8988
///
9089
/// * **Forward**(Status)
9190
///
@@ -177,9 +176,9 @@ pub type Outcome<S, E> = outcome::Outcome<S, (Status, E), Status>;
177176
///
178177
/// The type `T` is derived from the incoming request using `T`'s
179178
/// `FromRequest` implementation. If the derivation is a `Success`, the
180-
/// derived value is returned in `Some`. Otherwise, a `None` is returned.
181-
///
182-
/// _This implementation always returns successfully._
179+
/// derived value is returned in `Some`. If the derivation is a `Forward`,
180+
/// the result is `None`, and if the derivation is an `Error`, the `Error`
181+
/// is preserved.
183182
///
184183
/// * **Result&lt;T, T::Error>** _where_ **T: FromRequest**
185184
///
@@ -189,6 +188,14 @@ pub type Outcome<S, E> = outcome::Outcome<S, (Status, E), Status>;
189188
/// returned in `Err`. If the derivation is a `Forward`, the request is
190189
/// forwarded with the same status code as the original forward.
191190
///
191+
/// * **Outcome&lt;T, T::Error>** _where_ **T: FromRequest**
192+
///
193+
/// The type `T` is derived from the incoming request using `T`'s
194+
/// `FromRequest` implementation. The `Outcome` is then provided to the handler,
195+
/// reguardless of what it returned.
196+
///
197+
/// _This guard **always** succeeds_
198+
///
192199
/// [`Config`]: crate::config::Config
193200
///
194201
/// # Example
@@ -521,12 +528,13 @@ impl<'r, T: FromRequest<'r>> FromRequest<'r> for Result<T, T::Error> {
521528

522529
#[crate::async_trait]
523530
impl<'r, T: FromRequest<'r>> FromRequest<'r> for Option<T> {
524-
type Error = Infallible;
531+
type Error = T::Error;
525532

526-
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Infallible> {
533+
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
527534
match T::from_request(request).await {
528535
Success(val) => Success(Some(val)),
529-
Error(_) | Forward(_) => Success(None),
536+
Forward(_) => Success(None),
537+
Error(e) => Error(e)
530538
}
531539
}
532540
}

core/lib/tests/flash-lazy-removes-issue-466.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ fn set() -> Flash<&'static str> {
1111
}
1212

1313
#[get("/unused")]
14-
fn unused(flash: Option<FlashMessage<'_>>) -> Option<()> {
15-
flash.map(|_| ())
14+
fn unused(flash: Result<FlashMessage<'_>, ()>) -> Option<()> {
15+
flash.ok().map(|_| ())
1616
}
1717

1818
#[get("/use")]
19-
fn used(flash: Option<FlashMessage<'_>>) -> Option<String> {
20-
flash.map(|f| f.message().into())
19+
fn used(flash: Result<FlashMessage<'_>, ()>) -> Option<String> {
20+
flash.ok().map(|f| f.message().into())
2121
}
2222

2323
mod flash_lazy_remove_tests {

docs/guide/05-requests.md

+7-8
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ will be routed as follows:
270270
You'll also find a route's rank logged in brackets during application launch:
271271
`GET /user/<id> [3] (user_str)`.
272272

273-
Forwards can be _caught_ by using a `Result` or `Option` type. For example, if
273+
Forwards can be _caught_ by using a `Option` type. For example, if
274274
the type of `id` in the `user` function was `Result<usize, &str>`, then `user`
275275
would never forward. An `Ok` variant would indicate that `<id>` was a valid
276276
`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
279279
! tip: It's not just forwards that can be caught!
280280

281281
In general, when any guard fails for any reason, including parameter guards,
282-
you can use an `Option` or `Result` type in its place to catch the error.
282+
you can use a `Result` type in its place to catch the error.
283283

284284
By the way, if you were to omit the `rank` parameter in the `user_str` or
285285
`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.
511511
### Fallible Guards
512512

513513
A failing or forwarding guard can be "caught" in handler, preventing it from
514-
failing or forwarding, via the `Option<T>` and `Result<T, E>` guards. When a
515-
guard `T` fails or forwards, `Option<T>` will be `None`. If a guard `T` fails
514+
failing or forwarding, via `Result<T, E>` guards. If a guard `T` fails
516515
with error `E`, `Result<T, E>` will be `Err(E)`.
517516

518517
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> {
538537
}
539538
```
540539

541-
If the `User` guard forwards or fails, the `Option` will be `None`. If it
540+
If the `User` guard forwards, the `Option` will be `None`. If it
542541
succeeds, it will be `Some(User)`.
543542

544543
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
898897
into the `Task` structure. If the data that arrives isn't of the correct
899898
Content-Type, the request is forwarded. If the data doesn't parse or is simply
900899
invalid, a customizable error is returned. As before, a forward or error can
901-
be caught by using the `Option` and `Result` types:
900+
be caught by using the `Result` types:
902901

903902
```rust
904-
# use rocket::{post, form::Form};
903+
# use rocket::{post, form::{Form, Errors}};
905904
# type Task<'r> = &'r str;
906905

907906
#[post("/todo", data = "<task>")]
908-
fn new(task: Option<Form<Task<'_>>>) { /* .. */ }
907+
fn new(task: Result<Form<Task<'_>>, Errors<'_>>) { /* .. */ }
909908
```
910909

911910
### Multipart

docs/guide/06-responses.md

+6-3
Original file line numberDiff line numberDiff line change
@@ -616,18 +616,20 @@ For example, given the following route:
616616
```rust
617617
# #[macro_use] extern crate rocket;
618618
# fn main() {}
619+
# use std::num::ParseIntError;
619620

620621
#[get("/<id>/<name>?<age>")]
621-
fn person(id: Option<usize>, name: &str, age: Option<u8>) { /* .. */ }
622+
fn person(id: Result<usize, ParseIntError>, name: &str, age: Option<u8>) { /* .. */ }
622623
```
623624

624625
URIs to `person` can be created as follows:
625626

626627
```rust
627628
# #[macro_use] extern crate rocket;
629+
# use std::num::ParseIntError;
628630

629631
# #[get("/<id>/<name>?<age>")]
630-
# fn person(id: Option<usize>, name: &str, age: Option<u8>) { /* .. */ }
632+
# fn person(id: Result<usize, ParseIntError>, name: &str, age: Option<u8>) { /* .. */ }
631633

632634
// with unnamed parameters, in route path declaration order
633635
let mike = uri!(person(101, "Mike Smith", Some(28)));
@@ -755,9 +757,10 @@ generated.
755757

756758
```rust
757759
# #[macro_use] extern crate rocket;
760+
# use std::num::ParseIntError;
758761

759762
#[get("/<id>/<name>?<age>")]
760-
fn person(id: Option<usize>, name: &str, age: Option<u8>) { /* .. */ }
763+
fn person(id: Result<usize, ParseIntError>, name: &str, age: Option<u8>) { /* .. */ }
761764

762765
// Note that `id` is `Option<usize>` in the route, but `id` in `uri!` _cannot_
763766
// be an `Option`. `age`, on the other hand, _must_ be an `Option` (or `Result`

examples/cookies/src/session.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ fn login(_user: User) -> Redirect {
5353
}
5454

5555
#[get("/login", rank = 2)]
56-
fn login_page(flash: Option<FlashMessage<'_>>) -> Template {
57-
Template::render("login", flash)
56+
fn login_page(flash: Result<FlashMessage<'_>, ()>) -> Template {
57+
Template::render("login", flash.ok())
5858
}
5959

6060
#[post("/login", data = "<login>")]

examples/hello/src/main.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#[macro_use] extern crate rocket;
22

3+
use rocket::form::Errors;
4+
35
#[cfg(test)] mod tests;
46

57
#[derive(FromFormField)]
@@ -51,13 +53,13 @@ fn wave(name: &str, age: u8) -> String {
5153
// http://127.0.0.1:8000/?name=Rocketeer&lang=en&emoji
5254
// http://127.0.0.1:8000/?lang=ru&emoji&name=Rocketeer
5355
#[get("/?<lang>&<opt..>")]
54-
fn hello(lang: Option<Lang>, opt: Options<'_>) -> String {
56+
fn hello(lang: Result<Lang, Errors<'_>>, opt: Options<'_>) -> String {
5557
let mut greeting = String::new();
5658
if opt.emoji {
5759
greeting.push_str("👋 ");
5860
}
5961

60-
match lang {
62+
match lang.ok() {
6163
Some(Lang::Russian) => greeting.push_str("Привет"),
6264
Some(Lang::English) => greeting.push_str("Hello"),
6365
None => greeting.push_str("Hi"),

0 commit comments

Comments
 (0)