From 498af3d9d0d2c47dbd1ae5579f64c4be362e1d10 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Wed, 20 Sep 2023 13:55:59 -0700 Subject: [PATCH 01/22] Improve chat example JavaScript code. Resolves #2617. --- examples/chat/static/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/chat/static/script.js b/examples/chat/static/script.js index d78e3e6a3f..99e0fef25e 100644 --- a/examples/chat/static/script.js +++ b/examples/chat/static/script.js @@ -94,7 +94,7 @@ function subscribe(uri) { console.log("raw data", JSON.stringify(ev.data)); console.log("decoded data", JSON.stringify(JSON.parse(ev.data))); const msg = JSON.parse(ev.data); - if (!"message" in msg || !"room" in msg || !"username" in msg) return; + if (!("message" in msg) || !("room" in msg) || !("username" in msg)) return; addMessage(msg.room, msg.username, msg.message, true); }); From 2cf38a5aa37fe046e46b03740835787f0396307b Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Wed, 20 Sep 2023 15:33:51 -0700 Subject: [PATCH 02/22] Remove use of 'private_in_public' lint. The lint no longer exists. This gets rid of a compile-time warning for users. Resolves #2608. --- core/codegen/src/derive/from_form.rs | 5 ++++- core/codegen/tests/from_form.rs | 2 +- core/codegen/tests/uri_display.rs | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/core/codegen/src/derive/from_form.rs b/core/codegen/src/derive/from_form.rs index 24db4a0a70..19c4cc0267 100644 --- a/core/codegen/src/derive/from_form.rs +++ b/core/codegen/src/derive/from_form.rs @@ -123,7 +123,9 @@ pub fn derive_from_form(input: proc_macro::TokenStream) -> TokenStream { Ok(quote_spanned! { input.span() => /// Rocket generated FormForm context. #[doc(hidden)] - #[allow(private_in_public)] + #[allow(unknown_lints)] + #[allow(renamed_and_removed_lints)] + #[allow(private_in_public, private_bounds)] #vis struct #ctxt_ty #impl_gen #where_clause { __opts: #_form::Options, __errors: #_form::Errors<'r>, @@ -148,6 +150,7 @@ pub fn derive_from_form(input: proc_macro::TokenStream) -> TokenStream { #[allow(unused_imports)] use #_http::uncased::AsUncased; }) + .outer_mapper(quote!(#[allow(renamed_and_removed_lints)])) .outer_mapper(quote!(#[allow(private_in_public)])) .outer_mapper(quote!(#[rocket::async_trait])) .inner_mapper(MapperBuild::new() diff --git a/core/codegen/tests/from_form.rs b/core/codegen/tests/from_form.rs index 814ab8814d..621ffa0481 100644 --- a/core/codegen/tests/from_form.rs +++ b/core/codegen/tests/from_form.rs @@ -984,7 +984,7 @@ fn json_wrapper_works() { assert_eq!(form, JsonToken(Json("foo bar"))); } -// FIXME: https://github.com/rust-lang/rust/issues/86706 +#[allow(renamed_and_removed_lints)] #[allow(private_in_public)] struct Q(T); diff --git a/core/codegen/tests/uri_display.rs b/core/codegen/tests/uri_display.rs index 9ffd14362a..cce97817ad 100644 --- a/core/codegen/tests/uri_display.rs +++ b/core/codegen/tests/uri_display.rs @@ -247,7 +247,7 @@ fn uri_display_serde() { assert_query_value_roundtrip!(JsonFoo, JsonFoo(Json(bam.clone()))); - // FIXME: https://github.com/rust-lang/rust/issues/86706 + #[allow(renamed_and_removed_lints)] #[allow(private_in_public)] #[derive(Debug, PartialEq, Clone, FromForm, UriDisplayQuery)] struct Q(Json); From 28de15858e9cdbc88f1a10e53cde8fd98e7eda3c Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Wed, 20 Sep 2023 15:33:54 -0700 Subject: [PATCH 03/22] Avoid using 'macro' items on stable. This gets rid of the warning message on stable when building examples. --- core/codegen/Cargo.toml | 1 + core/codegen/src/bang/export.rs | 26 ++++++++++++++++++-------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/core/codegen/Cargo.toml b/core/codegen/Cargo.toml index 900fc4ad58..13b59390a4 100644 --- a/core/codegen/Cargo.toml +++ b/core/codegen/Cargo.toml @@ -23,6 +23,7 @@ proc-macro2 = "1.0.27" devise = "0.4" rocket_http = { version = "=0.5.0-rc.3", path = "../http/" } unicode-xid = "0.2" +version_check = "0.9" glob = "0.3" [dev-dependencies] diff --git a/core/codegen/src/bang/export.rs b/core/codegen/src/bang/export.rs index 5004d17aa0..c62b5e5238 100644 --- a/core/codegen/src/bang/export.rs +++ b/core/codegen/src/bang/export.rs @@ -31,6 +31,23 @@ pub fn _macro(input: proc_macro::TokenStream) -> devise::Result { }) .collect(); + // Only try using the `macro` syntax on nightly/dev or when we don't know. + let export = match version_check::is_feature_flaggable() { + Some(true) | None => quote! { + #(#attrs)* + #[cfg(all(nightly, doc))] + pub macro #macro_name { + #decl_macro_tokens + } + + #[cfg(not(all(nightly, doc)))] + pub use #mod_name::#internal_name as #macro_name; + }, + Some(false) => quote! { + pub use #mod_name::#internal_name as #macro_name; + } + }; + Ok(quote! { #[allow(non_snake_case)] mod #mod_name { @@ -43,13 +60,6 @@ pub fn _macro(input: proc_macro::TokenStream) -> devise::Result { pub use #internal_name; } - #(#attrs)* - #[cfg(all(nightly, doc))] - pub macro #macro_name { - #decl_macro_tokens - } - - #[cfg(not(all(nightly, doc)))] - pub use #mod_name::#internal_name as #macro_name; + #export }) } From bbb124eeea13368d3ebfdba097e6a212484c80a6 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Wed, 20 Sep 2023 16:15:57 -0700 Subject: [PATCH 04/22] Update UI tests for latest rustc. --- .../ui-fail-nightly/responder-types.stderr | 8 +- .../ui-fail-nightly/typed-uri-bad-type.stderr | 223 ++++++++++++++++-- .../tests/ui-fail-stable/from_form.stderr | 6 + 3 files changed, 211 insertions(+), 26 deletions(-) diff --git a/core/codegen/tests/ui-fail-nightly/responder-types.stderr b/core/codegen/tests/ui-fail-nightly/responder-types.stderr index fd26ecfebe..58956ef88f 100644 --- a/core/codegen/tests/ui-fail-nightly/responder-types.stderr +++ b/core/codegen/tests/ui-fail-nightly/responder-types.stderr @@ -1,8 +1,8 @@ error[E0277]: the trait bound `u8: Responder<'_, '_>` is not satisfied - --> tests/ui-fail-nightly/responder-types.rs:5:5 + --> tests/ui-fail-nightly/responder-types.rs:5:12 | 5 | thing: u8, - | ^^^^^^^^^ the trait `Responder<'_, '_>` is not implemented for `u8` + | ^^ the trait `Responder<'_, '_>` is not implemented for `u8` | = help: the following other types implement trait `Responder<'r, 'o>`: > @@ -39,10 +39,10 @@ note: required by a bound in `rocket::Response::<'r>::set_header` | ^^^^^^^^^^^^^^^^ required by this bound in `Response::<'r>::set_header` error[E0277]: the trait bound `u8: Responder<'_, '_>` is not satisfied - --> tests/ui-fail-nightly/responder-types.rs:16:5 + --> tests/ui-fail-nightly/responder-types.rs:16:12 | 16 | thing: u8, - | ^^^^^^^^^ the trait `Responder<'_, '_>` is not implemented for `u8` + | ^^ the trait `Responder<'_, '_>` is not implemented for `u8` | = help: the following other types implement trait `Responder<'r, 'o>`: > diff --git a/core/codegen/tests/ui-fail-nightly/typed-uri-bad-type.stderr b/core/codegen/tests/ui-fail-nightly/typed-uri-bad-type.stderr index 2e47e9046f..c54e50fd96 100644 --- a/core/codegen/tests/ui-fail-nightly/typed-uri-bad-type.stderr +++ b/core/codegen/tests/ui-fail-nightly/typed-uri-bad-type.stderr @@ -11,10 +11,10 @@ error[E0271]: type mismatch resolving `>::Error == &str` | ^^^^^^^^^^^^^^^^^^^^ expected `&str`, found `Empty` error[E0277]: the trait bound `usize: FromUriParam` is not satisfied - --> tests/ui-fail-nightly/typed-uri-bad-type.rs:45:22 + --> tests/ui-fail-nightly/typed-uri-bad-type.rs:45:5 | 45 | uri!(simple(id = "hi")); - | ^^^^ the trait `FromUriParam` is not implemented for `usize` + | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromUriParam` is not implemented for `usize` | = help: the following other types implement trait `FromUriParam`: > @@ -22,10 +22,10 @@ error[E0277]: the trait bound `usize: FromUriParam> error[E0277]: the trait bound `usize: FromUriParam` is not satisfied - --> tests/ui-fail-nightly/typed-uri-bad-type.rs:47:17 + --> tests/ui-fail-nightly/typed-uri-bad-type.rs:47:5 | 47 | uri!(simple("hello")); - | ^^^^^^^ the trait `FromUriParam` is not implemented for `usize` + | ^^^^^^^^^^^^^^^^^^^^^ the trait `FromUriParam` is not implemented for `usize` | = help: the following other types implement trait `FromUriParam`: > @@ -33,16 +33,33 @@ error[E0277]: the trait bound `usize: FromUriParam> error[E0277]: the trait bound `usize: FromUriParam` is not satisfied - --> tests/ui-fail-nightly/typed-uri-bad-type.rs:49:22 + --> tests/ui-fail-nightly/typed-uri-bad-type.rs:49:5 | 49 | uri!(simple(id = 239239i64)); - | ^^^^^^^^^ the trait `FromUriParam` is not implemented for `usize` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromUriParam` is not implemented for `usize` | = help: the following other types implement trait `FromUriParam`: > > > +error[E0277]: the trait bound `S: FromUriParam` is not satisfied + --> tests/ui-fail-nightly/typed-uri-bad-type.rs:51:5 + | +51 | uri!(not_uri_display(10, S)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromUriParam` is not implemented for `S` + | + = help: the following other types implement trait `FromUriParam`: + > + > + > + > + > + > + > + > + and $N others + error[E0277]: the trait bound `S: FromUriParam` is not satisfied --> tests/ui-fail-nightly/typed-uri-bad-type.rs:51:30 | @@ -61,10 +78,10 @@ error[E0277]: the trait bound `S: FromUriParam` and $N others error[E0277]: the trait bound `i32: FromUriParam>` is not satisfied - --> tests/ui-fail-nightly/typed-uri-bad-type.rs:56:25 + --> tests/ui-fail-nightly/typed-uri-bad-type.rs:56:5 | 56 | uri!(optionals(id = Some(10), name = Ok("bob".into()))); - | ^^^^^^^^ the trait `FromUriParam>` is not implemented for `i32` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromUriParam>` is not implemented for `i32` | = help: the following other types implement trait `FromUriParam`: > @@ -73,10 +90,10 @@ error[E0277]: the trait bound `i32: FromUriParam` to implement `FromUriParam>` error[E0277]: the trait bound `std::string::String: FromUriParam>` is not satisfied - --> tests/ui-fail-nightly/typed-uri-bad-type.rs:56:42 + --> tests/ui-fail-nightly/typed-uri-bad-type.rs:56:5 | 56 | uri!(optionals(id = Some(10), name = Ok("bob".into()))); - | ^^^^^^^^^^^^^^^^ the trait `FromUriParam>` is not implemented for `std::string::String` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromUriParam>` is not implemented for `std::string::String` | = help: the following other types implement trait `FromUriParam`: > @@ -88,10 +105,10 @@ error[E0277]: the trait bound `std::string::String: FromUriParam` to implement `FromUriParam>` error[E0277]: the trait bound `isize: FromUriParam` is not satisfied - --> tests/ui-fail-nightly/typed-uri-bad-type.rs:58:19 + --> tests/ui-fail-nightly/typed-uri-bad-type.rs:58:5 | 58 | uri!(simple_q("hi")); - | ^^^^ the trait `FromUriParam` is not implemented for `isize` + | ^^^^^^^^^^^^^^^^^^^^ the trait `FromUriParam` is not implemented for `isize` | = help: the following other types implement trait `FromUriParam`: > @@ -99,15 +116,32 @@ error[E0277]: the trait bound `isize: FromUriParam> error[E0277]: the trait bound `isize: FromUriParam` is not satisfied - --> tests/ui-fail-nightly/typed-uri-bad-type.rs:60:24 + --> tests/ui-fail-nightly/typed-uri-bad-type.rs:60:5 | 60 | uri!(simple_q(id = "hi")); - | ^^^^ the trait `FromUriParam` is not implemented for `isize` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromUriParam` is not implemented for `isize` + | + = help: the following other types implement trait `FromUriParam`: + > + > + > + +error[E0277]: the trait bound `S: FromUriParam` is not satisfied + --> tests/ui-fail-nightly/typed-uri-bad-type.rs:62:5 + | +62 | uri!(other_q(100, S)); + | ^^^^^^^^^^^^^^^^^^^^^ the trait `FromUriParam` is not implemented for `S` | = help: the following other types implement trait `FromUriParam`: + > + > + > > > > + > + > + and $N others error[E0277]: the trait bound `S: FromUriParam` is not satisfied --> tests/ui-fail-nightly/typed-uri-bad-type.rs:62:23 @@ -126,6 +160,23 @@ error[E0277]: the trait bound `S: FromUriParam > and $N others +error[E0277]: the trait bound `S: FromUriParam` is not satisfied + --> tests/ui-fail-nightly/typed-uri-bad-type.rs:64:5 + | +64 | uri!(other_q(rest = S, id = 100)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromUriParam` is not implemented for `S` + | + = help: the following other types implement trait `FromUriParam`: + > + > + > + > + > + > + > + > + and $N others + error[E0277]: the trait bound `S: FromUriParam` is not satisfied --> tests/ui-fail-nightly/typed-uri-bad-type.rs:64:25 | @@ -173,6 +224,23 @@ note: required by a bound in `assert_ignorable` | pub fn assert_ignorable>() { } | ^^^^^^^^^^^^ required by this bound in `assert_ignorable` +error[E0277]: the trait bound `S: FromUriParam` is not satisfied + --> tests/ui-fail-nightly/typed-uri-bad-type.rs:68:5 + | +68 | uri!(other_q(rest = S, id = _)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromUriParam` is not implemented for `S` + | + = help: the following other types implement trait `FromUriParam`: + > + > + > + > + > + > + > + > + and $N others + error[E0277]: the trait bound `S: FromUriParam` is not satisfied --> tests/ui-fail-nightly/typed-uri-bad-type.rs:68:25 | @@ -191,10 +259,10 @@ error[E0277]: the trait bound `S: FromUriParam and $N others error[E0277]: the trait bound `usize: FromUriParam` is not satisfied - --> tests/ui-fail-nightly/typed-uri-bad-type.rs:77:40 + --> tests/ui-fail-nightly/typed-uri-bad-type.rs:77:5 | 77 | uri!(uri!("?foo#bar"), simple(id = "hi")); - | ^^^^ the trait `FromUriParam` is not implemented for `usize` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromUriParam` is not implemented for `usize` | = help: the following other types implement trait `FromUriParam`: > @@ -220,10 +288,10 @@ note: required by a bound in `RouteUriBuilder::with_prefix` | ^^^^^^^^^^^^^^^^ required by this bound in `RouteUriBuilder::with_prefix` error[E0277]: the trait bound `usize: FromUriParam` is not satisfied - --> tests/ui-fail-nightly/typed-uri-bad-type.rs:78:33 + --> tests/ui-fail-nightly/typed-uri-bad-type.rs:78:5 | 78 | uri!(uri!("*"), simple(id = "hi")); - | ^^^^ the trait `FromUriParam` is not implemented for `usize` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromUriParam` is not implemented for `usize` | = help: the following other types implement trait `FromUriParam`: > @@ -249,10 +317,10 @@ note: required by a bound in `RouteUriBuilder::with_prefix` | ^^^^^^^^^^^^^^^^ required by this bound in `RouteUriBuilder::with_prefix` error[E0277]: the trait bound `usize: FromUriParam` is not satisfied - --> tests/ui-fail-nightly/typed-uri-bad-type.rs:81:25 + --> tests/ui-fail-nightly/typed-uri-bad-type.rs:81:5 | 81 | uri!(_, simple(id = "hi"), uri!("*")); - | ^^^^ the trait `FromUriParam` is not implemented for `usize` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromUriParam` is not implemented for `usize` | = help: the following other types implement trait `FromUriParam`: > @@ -282,10 +350,10 @@ note: required by a bound in `RouteUriBuilder::with_suffix` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `RouteUriBuilder::with_suffix` error[E0277]: the trait bound `usize: FromUriParam` is not satisfied - --> tests/ui-fail-nightly/typed-uri-bad-type.rs:82:25 + --> tests/ui-fail-nightly/typed-uri-bad-type.rs:82:5 | 82 | uri!(_, simple(id = "hi"), uri!("/foo/bar")); - | ^^^^ the trait `FromUriParam` is not implemented for `usize` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromUriParam` is not implemented for `usize` | = help: the following other types implement trait `FromUriParam`: > @@ -313,3 +381,114 @@ note: required by a bound in `RouteUriBuilder::with_suffix` | ----------- required by a bound in this associated function | where S: ValidRouteSuffix> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `RouteUriBuilder::with_suffix` + +error[E0277]: the trait bound `usize: FromUriParam` is not satisfied + --> tests/ui-fail-nightly/typed-uri-bad-type.rs:45:22 + | +45 | uri!(simple(id = "hi")); + | ^^^^ the trait `FromUriParam` is not implemented for `usize` + | + = help: the following other types implement trait `FromUriParam`: + > + > + > + +error[E0277]: the trait bound `usize: FromUriParam` is not satisfied + --> tests/ui-fail-nightly/typed-uri-bad-type.rs:47:17 + | +47 | uri!(simple("hello")); + | ^^^^^^^ the trait `FromUriParam` is not implemented for `usize` + | + = help: the following other types implement trait `FromUriParam`: + > + > + > + +error[E0277]: the trait bound `usize: FromUriParam` is not satisfied + --> tests/ui-fail-nightly/typed-uri-bad-type.rs:49:22 + | +49 | uri!(simple(id = 239239i64)); + | ^^^^^^^^^ the trait `FromUriParam` is not implemented for `usize` + | + = help: the following other types implement trait `FromUriParam`: + > + > + > + +error[E0277]: the trait bound `i32: FromUriParam>` is not satisfied + --> tests/ui-fail-nightly/typed-uri-bad-type.rs:56:25 + | +56 | uri!(optionals(id = Some(10), name = Ok("bob".into()))); + | ^^^^^^^^ the trait `FromUriParam>` is not implemented for `i32` + | + = help: the following other types implement trait `FromUriParam`: + > + > + > + = note: required for `std::option::Option` to implement `FromUriParam>` + +error[E0277]: the trait bound `isize: FromUriParam` is not satisfied + --> tests/ui-fail-nightly/typed-uri-bad-type.rs:58:19 + | +58 | uri!(simple_q("hi")); + | ^^^^ the trait `FromUriParam` is not implemented for `isize` + | + = help: the following other types implement trait `FromUriParam`: + > + > + > + +error[E0277]: the trait bound `isize: FromUriParam` is not satisfied + --> tests/ui-fail-nightly/typed-uri-bad-type.rs:60:24 + | +60 | uri!(simple_q(id = "hi")); + | ^^^^ the trait `FromUriParam` is not implemented for `isize` + | + = help: the following other types implement trait `FromUriParam`: + > + > + > + +error[E0277]: the trait bound `usize: FromUriParam` is not satisfied + --> tests/ui-fail-nightly/typed-uri-bad-type.rs:77:40 + | +77 | uri!(uri!("?foo#bar"), simple(id = "hi")); + | ^^^^ the trait `FromUriParam` is not implemented for `usize` + | + = help: the following other types implement trait `FromUriParam`: + > + > + > + +error[E0277]: the trait bound `usize: FromUriParam` is not satisfied + --> tests/ui-fail-nightly/typed-uri-bad-type.rs:78:33 + | +78 | uri!(uri!("*"), simple(id = "hi")); + | ^^^^ the trait `FromUriParam` is not implemented for `usize` + | + = help: the following other types implement trait `FromUriParam`: + > + > + > + +error[E0277]: the trait bound `usize: FromUriParam` is not satisfied + --> tests/ui-fail-nightly/typed-uri-bad-type.rs:81:25 + | +81 | uri!(_, simple(id = "hi"), uri!("*")); + | ^^^^ the trait `FromUriParam` is not implemented for `usize` + | + = help: the following other types implement trait `FromUriParam`: + > + > + > + +error[E0277]: the trait bound `usize: FromUriParam` is not satisfied + --> tests/ui-fail-nightly/typed-uri-bad-type.rs:82:25 + | +82 | uri!(_, simple(id = "hi"), uri!("/foo/bar")); + | ^^^^ the trait `FromUriParam` is not implemented for `usize` + | + = help: the following other types implement trait `FromUriParam`: + > + > + > diff --git a/core/codegen/tests/ui-fail-stable/from_form.stderr b/core/codegen/tests/ui-fail-stable/from_form.stderr index afd9e509c6..3bc006df65 100644 --- a/core/codegen/tests/ui-fail-stable/from_form.stderr +++ b/core/codegen/tests/ui-fail-stable/from_form.stderr @@ -523,6 +523,9 @@ help: the type constructed contains `{integer}` due to the type of the argument | ^^^ this argument influences the type of `Some` note: tuple variant defined here --> $RUST/core/src/option.rs + | + | Some(#[stable(feature = "rust1", since = "1.0.0")] T), + | ^^^^ error[E0308]: mismatched types --> tests/ui-fail-stable/from_form.rs:203:33 @@ -542,6 +545,9 @@ help: the type constructed contains `&'static str` due to the type of the argume | this argument influences the type of `Some` note: tuple variant defined here --> $RUST/core/src/option.rs + | + | Some(#[stable(feature = "rust1", since = "1.0.0")] T), + | ^^^^ error[E0277]: the trait bound `bool: From<&str>` is not satisfied --> tests/ui-fail-stable/from_form.rs:209:23 From f41474dd6136db0fe744907e06f8b38136259066 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Wed, 20 Sep 2023 17:17:22 -0700 Subject: [PATCH 05/22] Fix broken links between codegen and http crates. Also resolves link warnings emitted by rustdoc. Closes #2594 --- core/codegen/src/lib.rs | 1 + core/http/src/uri/fmt/from_uri_param.rs | 2 +- core/http/src/uri/fmt/uri_display.rs | 2 +- core/lib/src/response/mod.rs | 5 ++--- core/lib/src/response/stream/sse.rs | 6 +++--- core/lib/src/serde/json.rs | 2 +- core/lib/src/serde/msgpack.rs | 2 +- scripts/mk-docs.sh | 11 +++++++---- 8 files changed, 17 insertions(+), 14 deletions(-) diff --git a/core/codegen/src/lib.rs b/core/codegen/src/lib.rs index 08b06dea13..7cec0eb073 100644 --- a/core/codegen/src/lib.rs +++ b/core/codegen/src/lib.rs @@ -1457,6 +1457,7 @@ pub fn catchers(input: TokenStream) -> TokenStream { /// are not ignorable. /// /// [`Uri`]: ../rocket/http/uri/enum.Uri.html +/// [`Uri::parse_any()`]: ../rocket/http/uri/enum.Uri.html#method.parse_any /// [`Origin`]: ../rocket/http/uri/struct.Origin.html /// [`Asterisk`]: ../rocket/http/uri/struct.Asterisk.html /// [`Authority`]: ../rocket/http/uri/struct.Authority.html diff --git a/core/http/src/uri/fmt/from_uri_param.rs b/core/http/src/uri/fmt/from_uri_param.rs index 0850b905cc..ae162e9bd2 100644 --- a/core/http/src/uri/fmt/from_uri_param.rs +++ b/core/http/src/uri/fmt/from_uri_param.rs @@ -178,7 +178,7 @@ use crate::uri::fmt::{self, Part}; /// assert_eq!(uri.query().unwrap(), "name=Robert%20Mike&nickname=Bob"); /// ``` /// -/// [`uri!`]: rocket::uri +/// [`uri!`]: ../../../../rocket/macro.uri.html /// [`FromUriParam::Target`]: crate::uri::fmt::FromUriParam::Target /// [`Path`]: crate::uri::fmt::Path /// [`Query`]: crate::uri::fmt::Query diff --git a/core/http/src/uri/fmt/uri_display.rs b/core/http/src/uri/fmt/uri_display.rs index fb8902ff02..b8e630e09a 100644 --- a/core/http/src/uri/fmt/uri_display.rs +++ b/core/http/src/uri/fmt/uri_display.rs @@ -106,7 +106,7 @@ use crate::uri::fmt::{Part, Path, Query, Formatter}; /// seen, the implementations will be used to display the value in a URI-safe /// manner. /// -/// [`uri!`]: rocket::uri +/// [`uri!`]: ../../../../rocket/macro.uri.html /// /// # Provided Implementations /// diff --git a/core/lib/src/response/mod.rs b/core/lib/src/response/mod.rs index 22b26eb685..71f0ff6980 100644 --- a/core/lib/src/response/mod.rs +++ b/core/lib/src/response/mod.rs @@ -1,9 +1,8 @@ //! Types and traits to build and send responses. //! //! The return type of a Rocket handler can be any type that implements the -//! [`Responder`](crate::response::Responder) trait, which means that the type knows -//! how to generate a [`Response`]. Among other things, this module contains -//! several such types. +//! [`Responder`] trait, which means that the type knows how to generate a +//! [`Response`]. Among other things, this module contains several such types. //! //! # Composing //! diff --git a/core/lib/src/response/stream/sse.rs b/core/lib/src/response/stream/sse.rs index 3177497221..b123f632d6 100644 --- a/core/lib/src/response/stream/sse.rs +++ b/core/lib/src/response/stream/sse.rs @@ -351,9 +351,9 @@ impl Event { /// # Responder /// /// `EventStream` is a (potentially infinite) responder. The response -/// `Content-Type` is set to [`EventStream`](ContentType::EventStream). The body -/// is [unsized](crate::response::Body#unsized), and values are sent as soon as -/// they are yielded by the internal iterator. +/// `Content-Type` is set to [`EventStream`](const@ContentType::EventStream). +/// The body is [unsized](crate::response::Body#unsized), and values are sent as +/// soon as they are yielded by the internal iterator. /// /// ## Heartbeat /// diff --git a/core/lib/src/serde/json.rs b/core/lib/src/serde/json.rs index 3f8ffe5f23..dfc85a6ba1 100644 --- a/core/lib/src/serde/json.rs +++ b/core/lib/src/serde/json.rs @@ -1,6 +1,6 @@ //! Automatic JSON (de)serialization support. //! -//! See [`Json`](Json) for details. +//! See [`Json`] for details. //! //! # Enabling //! diff --git a/core/lib/src/serde/msgpack.rs b/core/lib/src/serde/msgpack.rs index bb9867e313..e77c6aa76f 100644 --- a/core/lib/src/serde/msgpack.rs +++ b/core/lib/src/serde/msgpack.rs @@ -1,6 +1,6 @@ //! Automatic MessagePack (de)serialization support. //! -//! See [`MsgPack`](crate::serde::msgpack::MsgPack) for further details. +//! See [`MsgPack`] for further details. //! //! # Enabling //! diff --git a/scripts/mk-docs.sh b/scripts/mk-docs.sh index 93659d091d..ed81362540 100755 --- a/scripts/mk-docs.sh +++ b/scripts/mk-docs.sh @@ -20,10 +20,13 @@ fi echo ":::: Generating the docs..." pushd "${PROJECT_ROOT}" > /dev/null 2>&1 # Set the crate version and fill in missing doc URLs with docs.rs links. - RUSTDOCFLAGS="-Zunstable-options --crate-version ${DOC_VERSION}" \ - cargo doc -p rocket \ - -p rocket_sync_db_pools -p rocket_dyn_templates -p rocket_db_pools -p rocket_ws \ - -Zrustdoc-map --no-deps --all-features + RUSTDOCFLAGS="-Zunstable-options --crate-version ${DOC_VERSION} --extern-html-root-url rocket=https://api.rocket.rs/rocket/" \ + cargo doc -Zrustdoc-map --no-deps --all-features \ + -p rocket \ + -p rocket_db_pools \ + -p rocket_sync_db_pools \ + -p rocket_dyn_templates \ + -p rocket_ws popd > /dev/null 2>&1 # Blank index, for redirection. From 5d31ad4efb9e86c113ec4385a92392eef298d27f Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Thu, 28 Sep 2023 23:50:29 -0700 Subject: [PATCH 06/22] Update 'cookie' to 0.18. --- core/codegen/src/bang/uri_parsing.rs | 2 +- core/http/Cargo.toml | 2 +- core/lib/src/cookies.rs | 73 +++++++++++-------- core/lib/src/local/asynchronous/request.rs | 4 +- core/lib/src/local/blocking/request.rs | 2 +- core/lib/src/local/request.rs | 26 ++++--- core/lib/src/request/request.rs | 4 +- core/lib/src/response/flash.rs | 6 +- core/lib/tests/catcher-cookies-1213.rs | 8 +- core/lib/tests/cookies-private.rs | 14 ++-- .../local_request_private_cookie-issue-368.rs | 6 +- core/lib/tests/many-cookie-jars-at-once.rs | 10 +-- core/lib/tests/session-cookies-issue-1506.rs | 3 +- core/lib/tests/untracked-vs-tracked.rs | 4 +- examples/cookies/src/message.rs | 9 +-- examples/cookies/src/session.rs | 4 +- examples/tls/src/tests.rs | 8 +- site/guide/4-requests.md | 2 +- 18 files changed, 99 insertions(+), 88 deletions(-) diff --git a/core/codegen/src/bang/uri_parsing.rs b/core/codegen/src/bang/uri_parsing.rs index ea3665a824..efbe41a2d3 100644 --- a/core/codegen/src/bang/uri_parsing.rs +++ b/core/codegen/src/bang/uri_parsing.rs @@ -430,7 +430,7 @@ impl Arg { fn unnamed(&self) -> &ArgExpr { match self { Arg::Unnamed(expr) => expr, - _ => panic!("Called Arg::unnamed() on an Arg::named!"), + _ => panic!("Called Arg::unnamed() on an Arg::Named!"), } } diff --git a/core/http/Cargo.toml b/core/http/Cargo.toml index 2aa700f01c..41fd3813b8 100644 --- a/core/http/Cargo.toml +++ b/core/http/Cargo.toml @@ -42,7 +42,7 @@ pear = "0.2.3" pin-project-lite = "0.2" memchr = "2" stable-pattern = "0.1" -cookie = { version = "0.17.0", features = ["percent-encode"] } +cookie = { version = "=0.18.0-rc.0", features = ["percent-encode"] } state = "0.6" futures = { version = "0.3", default-features = false } diff --git a/core/lib/src/cookies.rs b/core/lib/src/cookies.rs index 62515c055b..3e7df583e1 100644 --- a/core/lib/src/cookies.rs +++ b/core/lib/src/cookies.rs @@ -28,19 +28,19 @@ pub use self::cookie::{Cookie, SameSite, Iter}; /// /// #[get("/message")] /// fn message(jar: &CookieJar<'_>) { -/// jar.add(Cookie::new("message", "hello!")); -/// jar.add(Cookie::new("other", "bye!")); +/// jar.add(("message", "hello!")); +/// jar.add(Cookie::build(("session", "bye!")).expires(None)); /// /// // `get()` does not reflect changes. -/// assert!(jar.get("other").is_none()); -/// # assert_eq!(jar.get("message").map(|c| c.value()), Some("hi")); +/// assert!(jar.get("session").is_none()); +/// assert_eq!(jar.get("message").map(|c| c.value()), Some("hi")); /// /// // `get_pending()` does. -/// let other_pending = jar.get_pending("other"); +/// let session_pending = jar.get_pending("session"); /// let message_pending = jar.get_pending("message"); -/// assert_eq!(other_pending.as_ref().map(|c| c.value()), Some("bye!")); +/// assert_eq!(session_pending.as_ref().map(|c| c.value()), Some("bye!")); /// assert_eq!(message_pending.as_ref().map(|c| c.value()), Some("hello!")); -/// # jar.remove(Cookie::named("message")); +/// # jar.remove("message"); /// # assert_eq!(jar.get("message").map(|c| c.value()), Some("hi")); /// # assert!(jar.get_pending("message").is_none()); /// } @@ -48,7 +48,7 @@ pub use self::cookie::{Cookie, SameSite, Iter}; /// # use rocket::local::blocking::Client; /// # let client = Client::debug_with(routes![message]).unwrap(); /// # let response = client.get("/message") -/// # .cookie(Cookie::new("message", "hi")) +/// # .cookie(("message", "hi")) /// # .dispatch(); /// # /// # assert!(response.status().class().is_success()); @@ -202,7 +202,7 @@ impl<'a> CookieJar<'a> { /// /// ```rust /// # #[macro_use] extern crate rocket; - /// use rocket::http::{Cookie, CookieJar}; + /// use rocket::http::CookieJar; /// /// #[get("/")] /// fn handler(jar: &CookieJar<'_>) { @@ -226,7 +226,7 @@ impl<'a> CookieJar<'a> { /// /// ```rust /// # #[macro_use] extern crate rocket; - /// use rocket::http::{Cookie, CookieJar}; + /// use rocket::http::CookieJar; /// /// #[get("/")] /// fn handler(jar: &CookieJar<'_>) { @@ -252,7 +252,7 @@ impl<'a> CookieJar<'a> { /// /// ```rust /// # #[macro_use] extern crate rocket; - /// use rocket::http::{Cookie, CookieJar}; + /// use rocket::http::CookieJar; /// /// #[get("/")] /// fn handler(jar: &CookieJar<'_>) { @@ -297,17 +297,18 @@ impl<'a> CookieJar<'a> { /// /// #[get("/")] /// fn handler(jar: &CookieJar<'_>) { - /// jar.add(Cookie::new("first", "value")); + /// jar.add(("first", "value")); /// - /// let cookie = Cookie::build("other", "value_two") + /// let cookie = Cookie::build(("other", "value_two")) /// .path("/") /// .secure(true) /// .same_site(SameSite::Lax); /// - /// jar.add(cookie.finish()); + /// jar.add(cookie); /// } /// ``` - pub fn add(&self, mut cookie: Cookie<'static>) { + pub fn add>>(&self, cookie: C) { + let mut cookie = cookie.into(); Self::set_defaults(self.config, &mut cookie); self.ops.lock().push(Op::Add(cookie, false)); } @@ -334,30 +335,32 @@ impl<'a> CookieJar<'a> { /// /// ```rust /// # #[macro_use] extern crate rocket; - /// use rocket::http::{Cookie, CookieJar}; + /// use rocket::http::CookieJar; /// /// #[get("/")] /// fn handler(jar: &CookieJar<'_>) { - /// jar.add_private(Cookie::new("name", "value")); + /// jar.add_private(("name", "value")); /// } /// ``` #[cfg(feature = "secrets")] #[cfg_attr(nightly, doc(cfg(feature = "secrets")))] - pub fn add_private(&self, mut cookie: Cookie<'static>) { + pub fn add_private>>(&self, cookie: C) { + let mut cookie = cookie.into(); Self::set_private_defaults(self.config, &mut cookie); self.ops.lock().push(Op::Add(cookie, true)); } /// Removes `cookie` from this collection and generates a "removal" cookies - /// to send to the client on response. For correctness, `cookie` must - /// contain the same `path` and `domain` as the cookie that was initially - /// set. Failure to provide the initial `path` and `domain` will result in - /// cookies that are not properly removed. For convenience, if a path is not - /// set on `cookie`, the `"/"` path will automatically be set. + /// to send to the client on response. A "removal" cookie is a cookie that + /// has the same name as the original cookie but has an empty value, a + /// max-age of 0, and an expiration date far in the past. + /// + /// **Note: For correctness, `cookie` must contain the same `path` and + /// `domain` as the cookie that was initially set. Failure to provide the + /// initial `path` and `domain` will result in cookies that are not properly + /// removed. For convenience, if a path is not set on `cookie`, the `"/"` + /// path will automatically be set.** /// - /// A "removal" cookie is a cookie that has the same name as the original - /// cookie but has an empty value, a max-age of 0, and an expiration date - /// far in the past. /// /// # Example /// @@ -367,10 +370,15 @@ impl<'a> CookieJar<'a> { /// /// #[get("/")] /// fn handler(jar: &CookieJar<'_>) { - /// jar.remove(Cookie::named("name")); + /// // Rocket will set `path` to `/`. + /// jar.remove("name"); + /// + /// // Use a custom-built cookie to set a custom path. + /// jar.remove(Cookie::build("name").path("/login")); /// } /// ``` - pub fn remove(&self, mut cookie: Cookie<'static>) { + pub fn remove>>(&self, cookie: C) { + let mut cookie = cookie.into(); if cookie.path().is_none() { cookie.set_path("/"); } @@ -388,16 +396,17 @@ impl<'a> CookieJar<'a> { /// /// ```rust /// # #[macro_use] extern crate rocket; - /// use rocket::http::{Cookie, CookieJar}; + /// use rocket::http::CookieJar; /// /// #[get("/")] /// fn handler(jar: &CookieJar<'_>) { - /// jar.remove_private(Cookie::named("name")); + /// jar.remove_private("name"); /// } /// ``` #[cfg(feature = "secrets")] #[cfg_attr(nightly, doc(cfg(feature = "secrets")))] - pub fn remove_private(&self, mut cookie: Cookie<'static>) { + pub fn remove_private>>(&self, cookie: C) { + let mut cookie = cookie.into(); if cookie.path().is_none() { cookie.set_path("/"); } @@ -415,7 +424,7 @@ impl<'a> CookieJar<'a> { /// /// ```rust /// # #[macro_use] extern crate rocket; - /// use rocket::http::{Cookie, CookieJar}; + /// use rocket::http::CookieJar; /// /// #[get("/")] /// fn handler(jar: &CookieJar<'_>) { diff --git a/core/lib/src/local/asynchronous/request.rs b/core/lib/src/local/asynchronous/request.rs index 994472536a..76ed3f3707 100644 --- a/core/lib/src/local/asynchronous/request.rs +++ b/core/lib/src/local/asynchronous/request.rs @@ -24,7 +24,7 @@ use super::{Client, LocalResponse}; /// let req = client.post("/") /// .header(ContentType::JSON) /// .remote("127.0.0.1:8000".parse().unwrap()) -/// .cookie(Cookie::new("name", "value")) +/// .cookie(("name", "value")) /// .body(r#"{ "value": 42 }"#); /// /// let response = req.dispatch().await; @@ -106,7 +106,7 @@ impl<'c> LocalRequest<'c> { for cookie in response.cookies().iter() { if let Some(expires) = cookie.expires_datetime() { if expires <= current_time { - jar.force_remove(cookie); + jar.force_remove(cookie.name()); continue; } } diff --git a/core/lib/src/local/blocking/request.rs b/core/lib/src/local/blocking/request.rs index db2f494494..f094c60e44 100644 --- a/core/lib/src/local/blocking/request.rs +++ b/core/lib/src/local/blocking/request.rs @@ -22,7 +22,7 @@ use super::{Client, LocalResponse}; /// let req = client.post("/") /// .header(ContentType::JSON) /// .remote("127.0.0.1:8000".parse().unwrap()) -/// .cookie(Cookie::new("name", "value")) +/// .cookie(("name", "value")) /// .body(r#"{ "value": 42 }"#); /// /// let response = req.dispatch(); diff --git a/core/lib/src/local/request.rs b/core/lib/src/local/request.rs index 4ba38634c8..a04ff5d7ba 100644 --- a/core/lib/src/local/request.rs +++ b/core/lib/src/local/request.rs @@ -131,13 +131,15 @@ macro_rules! pub_request_impl { /// # Client::_test(|_, request, _| { /// let request: LocalRequest = request; /// let req = request - /// .cookie(Cookie::new("username", "sb")) - /// .cookie(Cookie::new("user_id", "12")); + /// .cookie(("username", "sb")) + /// .cookie(("user_id", "12")); /// # }); /// ``` #[inline] - pub fn cookie(mut self, cookie: crate::http::Cookie<'_>) -> Self { - self._request_mut().cookies_mut().add_original(cookie.into_owned()); + pub fn cookie<'a, C>(mut self, cookie: C) -> Self + where C: Into> + { + self._request_mut().cookies_mut().add_original(cookie.into().into_owned()); self } @@ -151,15 +153,17 @@ macro_rules! pub_request_impl { /// /// # Client::_test(|_, request, _| { /// let request: LocalRequest = request; - /// let cookies = vec![Cookie::new("a", "b"), Cookie::new("c", "d")]; + /// let cookies = vec![("a", "b"), ("c", "d")]; /// let req = request.cookies(cookies); /// # }); /// ``` #[inline] - pub fn cookies<'a, C>(mut self, cookies: C) -> Self - where C: IntoIterator> + pub fn cookies<'a, C, I>(mut self, cookies: I) -> Self + where C: Into>, + I: IntoIterator { for cookie in cookies { + let cookie: crate::http::Cookie<'_> = cookie.into(); self._request_mut().cookies_mut().add_original(cookie.into_owned()); } @@ -180,14 +184,16 @@ macro_rules! pub_request_impl { /// /// # Client::_test(|_, request, _| { /// let request: LocalRequest = request; - /// let req = request.private_cookie(Cookie::new("user_id", "sb")); + /// let req = request.private_cookie(("user_id", "sb")); /// # }); /// ``` #[cfg(feature = "secrets")] #[cfg_attr(nightly, doc(cfg(feature = "secrets")))] #[inline] - pub fn private_cookie(mut self, cookie: crate::http::Cookie<'static>) -> Self { - self._request_mut().cookies_mut().add_original_private(cookie); + pub fn private_cookie(mut self, cookie: C) -> Self + where C: Into> + { + self._request_mut().cookies_mut().add_original_private(cookie.into()); self } diff --git a/core/lib/src/request/request.rs b/core/lib/src/request/request.rs index 62a2bb1e62..7f7e50e7eb 100644 --- a/core/lib/src/request/request.rs +++ b/core/lib/src/request/request.rs @@ -446,8 +446,8 @@ impl<'r> Request<'r> { /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap(); /// # let request = c.get("/"); /// # let req = request.inner(); - /// req.cookies().add(Cookie::new("key", "val")); - /// req.cookies().add(Cookie::new("ans", format!("life: {}", 38 + 4))); + /// req.cookies().add(("key", "val")); + /// req.cookies().add(("ans", format!("life: {}", 38 + 4))); /// /// assert_eq!(req.cookies().get_pending("key").unwrap().value(), "val"); /// assert_eq!(req.cookies().get_pending("ans").unwrap().value(), "life: 42"); diff --git a/core/lib/src/response/flash.rs b/core/lib/src/response/flash.rs index dc66545801..187223ab4f 100644 --- a/core/lib/src/response/flash.rs +++ b/core/lib/src/response/flash.rs @@ -178,9 +178,9 @@ impl Flash { let content = format!("{}{}{}{}", self.kind.len(), FLASH_COOKIE_DELIM, self.kind, self.message); - Cookie::build(FLASH_COOKIE_NAME, content) + Cookie::build((FLASH_COOKIE_NAME, content)) .max_age(Duration::minutes(5)) - .finish() + .build() } } @@ -211,7 +211,7 @@ impl<'r> FlashMessage<'r> { fn clear_cookie_if_needed(&self) { // Remove the cookie if it hasn't already been removed. if !self.consumed.swap(true, Ordering::Relaxed) { - self.inner.remove(Cookie::named(FLASH_COOKIE_NAME)); + self.inner.remove(FLASH_COOKIE_NAME); } } diff --git a/core/lib/tests/catcher-cookies-1213.rs b/core/lib/tests/catcher-cookies-1213.rs index d8d09f1730..0b7e81d474 100644 --- a/core/lib/tests/catcher-cookies-1213.rs +++ b/core/lib/tests/catcher-cookies-1213.rs @@ -1,17 +1,17 @@ #[macro_use] extern crate rocket; use rocket::request::Request; -use rocket::http::{Cookie, CookieJar}; +use rocket::http::CookieJar; #[catch(404)] fn not_found(request: &Request) -> &'static str { - request.cookies().add(Cookie::new("not_found", "404")); + request.cookies().add(("not_found", "404")); "404 - Not Found" } #[get("/")] fn index(cookies: &CookieJar<'_>) -> &'static str { - cookies.add(Cookie::new("index", "hi")); + cookies.add(("index", "hi")); "Hello, world!" } @@ -26,7 +26,7 @@ mod tests { .mount("/", routes![index]) .register("/", catchers![not_found]) .attach(AdHoc::on_request("Add Cookie", |req, _| Box::pin(async move { - req.cookies().add(Cookie::new("fairing", "woo")); + req.cookies().add(("fairing", "woo")); }))); let client = Client::debug(rocket).unwrap(); diff --git a/core/lib/tests/cookies-private.rs b/core/lib/tests/cookies-private.rs index e499ed34e8..c6e11a247b 100644 --- a/core/lib/tests/cookies-private.rs +++ b/core/lib/tests/cookies-private.rs @@ -10,7 +10,7 @@ fn cookie_add_private(jar: &CookieJar<'_>) { jar.add(cookie_a.clone()); let mut cookie_b = Cookie::new("b", "v2"); jar.add_private(cookie_b.clone()); - jar.add(Cookie::new("c", "v3")); + jar.add(("c", "v3")); // private: CookieJar::set_defaults(&mut cookie_a); cookie_a.set_path("/"); @@ -89,9 +89,9 @@ mod cookies_private_tests { let client = Client::debug(rocket()).unwrap(); let response = client .get("/") - .cookie(Cookie::new("a", "Cookie")) - .private_cookie(Cookie::new("b", " tastes ")) - .cookie(Cookie::new("c", "good!")) + .cookie(("a", "Cookie")) + .private_cookie(("b", " tastes ")) + .cookie(("c", "good!")) .dispatch(); assert_eq!(response.into_string().unwrap(), "Cookie tastes good!"); @@ -103,9 +103,9 @@ mod cookies_private_tests { let client = Client::debug(rocket()).unwrap(); let response = client .get("/oh-no") - .cookie(Cookie::new("a", "Cookie")) - .private_cookie(Cookie::new("b", " tastes ")) - .cookie(Cookie::new("c", "good!")) + .cookie(("a", "Cookie")) + .private_cookie(("b", " tastes ")) + .cookie(("c", "good!")) .dispatch(); assert_ne!(response.into_string().unwrap(), "Cookie tastes good!"); diff --git a/core/lib/tests/local_request_private_cookie-issue-368.rs b/core/lib/tests/local_request_private_cookie-issue-368.rs index 2a516f2ad6..34b98ce9db 100644 --- a/core/lib/tests/local_request_private_cookie-issue-368.rs +++ b/core/lib/tests/local_request_private_cookie-issue-368.rs @@ -14,14 +14,14 @@ mod tests { use super::*; use rocket::routes; use rocket::local::blocking::Client; - use rocket::http::{Cookie, Status}; + use rocket::http::Status; #[test] fn private_cookie_is_returned() { let rocket = rocket::build().mount("/", routes![return_private_cookie]); let client = Client::debug(rocket).unwrap(); - let req = client.get("/").private_cookie(Cookie::new("cookie_name", "cookie_value")); + let req = client.get("/").private_cookie(("cookie_name", "cookie_value")); let response = req.dispatch(); assert_eq!(response.headers().get_one("Set-Cookie"), None); @@ -33,7 +33,7 @@ mod tests { let rocket = rocket::build().mount("/", routes![return_private_cookie]); let client = Client::debug(rocket).unwrap(); - let req = client.get("/").cookie(Cookie::new("cookie_name", "cookie_value")); + let req = client.get("/").cookie(("cookie_name", "cookie_value")); let response = req.dispatch(); assert_eq!(response.status(), Status::NotFound); diff --git a/core/lib/tests/many-cookie-jars-at-once.rs b/core/lib/tests/many-cookie-jars-at-once.rs index 5e8a9ff4a5..781daffacf 100644 --- a/core/lib/tests/many-cookie-jars-at-once.rs +++ b/core/lib/tests/many-cookie-jars-at-once.rs @@ -1,11 +1,11 @@ #[macro_use] extern crate rocket; -use rocket::http::{Cookie, CookieJar}; +use rocket::http::CookieJar; #[post("/")] fn multi_add(jar_a: &CookieJar<'_>, jar_b: &CookieJar<'_>) { - jar_a.add(Cookie::new("a", "v1")); - jar_b.add(Cookie::new("b", "v2")); + jar_a.add(("a", "v1")); + jar_b.add(("b", "v2")); } #[get("/")] @@ -41,8 +41,8 @@ mod many_cookie_jars_tests { fn test_multi_get() { let client = Client::debug(rocket()).unwrap(); let response = client.get("/") - .cookie(Cookie::new("a", "a_val")) - .cookie(Cookie::new("b", "hi!")) + .cookie(("a", "a_val")) + .cookie(("b", "hi!")) .dispatch(); assert_eq!(response.into_string().unwrap(), "a_valhi!"); diff --git a/core/lib/tests/session-cookies-issue-1506.rs b/core/lib/tests/session-cookies-issue-1506.rs index 5166214079..7ae72526c7 100644 --- a/core/lib/tests/session-cookies-issue-1506.rs +++ b/core/lib/tests/session-cookies-issue-1506.rs @@ -4,8 +4,7 @@ use rocket::http::{CookieJar, Cookie}; #[rocket::get("/")] fn index(jar: &CookieJar<'_>) { - let session_cookie = Cookie::build("key", "value").expires(None); - jar.add_private(session_cookie.finish()); + jar.add_private(Cookie::build(("key", "value")).expires(None)); } mod test_session_cookies { diff --git a/core/lib/tests/untracked-vs-tracked.rs b/core/lib/tests/untracked-vs-tracked.rs index c9df4f24ef..343fb3ee11 100644 --- a/core/lib/tests/untracked-vs-tracked.rs +++ b/core/lib/tests/untracked-vs-tracked.rs @@ -1,10 +1,10 @@ #[macro_use] extern crate rocket; -use rocket::http::{Cookie, CookieJar}; +use rocket::http::CookieJar; #[post("/")] fn add(jar: &CookieJar<'_>) { - jar.add(Cookie::new("name", "value")); + jar.add(("name", "value")); } #[get("/")] diff --git a/examples/cookies/src/message.rs b/examples/cookies/src/message.rs index aa1fb0d353..d4896adbf6 100644 --- a/examples/cookies/src/message.rs +++ b/examples/cookies/src/message.rs @@ -12,17 +12,14 @@ pub use message_uri as uri; #[post("/", data = "")] fn submit(cookies: &CookieJar<'_>, message: Form<&str>) -> Redirect { - cookies.add(Cookie::new("message", message.to_string())); + cookies.add(("message", message.to_string())); Redirect::to(uri!(index)) } #[get("/")] fn index(cookies: &CookieJar<'_>) -> Template { - let cookie = cookies.get("message"); - - Template::render("message", context! { - message: cookie.map(|c| c.value()), - }) + let message = cookies.get("message").map(|c| c.value()); + Template::render("message", context! { message }) } pub fn routes() -> Vec { diff --git a/examples/cookies/src/session.rs b/examples/cookies/src/session.rs index c4309a4283..d66a5911aa 100644 --- a/examples/cookies/src/session.rs +++ b/examples/cookies/src/session.rs @@ -60,7 +60,7 @@ fn login_page(flash: Option>) -> Template { #[post("/login", data = "")] fn post_login(jar: &CookieJar<'_>, login: Form>) -> Result> { if login.username == "Sergio" && login.password == "password" { - jar.add_private(Cookie::new("user_id", 1.to_string())); + jar.add_private(("user_id", "1")); Ok(Redirect::to(uri!(index))) } else { Err(Flash::error(Redirect::to(uri!(login_page)), "Invalid username/password.")) @@ -69,7 +69,7 @@ fn post_login(jar: &CookieJar<'_>, login: Form>) -> Result) -> Flash { - jar.remove_private(Cookie::named("user_id")); + jar.remove_private("user_id"); Flash::success(Redirect::to(uri!(login_page)), "Successfully logged out.") } diff --git a/examples/tls/src/tests.rs b/examples/tls/src/tests.rs index 2f48af0274..2629e3c487 100644 --- a/examples/tls/src/tests.rs +++ b/examples/tls/src/tests.rs @@ -27,11 +27,11 @@ fn secure_cookies() { #[get("/cookie")] fn cookie(jar: &CookieJar<'_>) { - jar.add(Cookie::new("k1", "v1")); - jar.add_private(Cookie::new("k2", "v2")); + jar.add(("k1", "v1")); + jar.add_private(("k2", "v2")); - jar.add(Cookie::build("k1u", "v1u").secure(false).finish()); - jar.add_private(Cookie::build("k2u", "v2u").secure(false).finish()); + jar.add(Cookie::build(("k1u", "v1u")).secure(false)); + jar.add_private(Cookie::build(("k2u", "v2u")).secure(false)); } let client = Client::tracked(super::rocket().mount("/", routes![cookie])).unwrap(); diff --git a/site/guide/4-requests.md b/site/guide/4-requests.md index 4fe8fee5b7..1a1a8c637e 100644 --- a/site/guide/4-requests.md +++ b/site/guide/4-requests.md @@ -631,7 +631,7 @@ fn user_id(cookies: &CookieJar<'_>) -> Option { /// Remove the `user_id` cookie. #[post("/logout")] fn logout(cookies: &CookieJar<'_>) -> Flash { - cookies.remove_private(Cookie::named("user_id")); + cookies.remove_private("user_id"); Flash::success(Redirect::to("/"), "Successfully logged out.") } ``` From 47faac6080b5ce9d6ec33a06ccb6a914b8054966 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Mon, 2 Oct 2023 11:59:03 -0700 Subject: [PATCH 07/22] Document that '&[u8]' is a form guard. --- core/lib/src/form/from_form.rs | 4 +++- core/lib/src/form/from_form_field.rs | 23 ++++++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/core/lib/src/form/from_form.rs b/core/lib/src/form/from_form.rs index 726ca208f6..f44be8cf99 100644 --- a/core/lib/src/form/from_form.rs +++ b/core/lib/src/form/from_form.rs @@ -120,7 +120,7 @@ use crate::http::uncased::AsUncased; /// | IP Address | _inherit_ | **no default** | No | Yes | [`IpAddr`], [`Ipv4Addr`], [`Ipv6Addr`] | /// | Socket Address | _inherit_ | **no default** | No | Yes | [`SocketAddr`], [`SocketAddrV4`], [`SocketAddrV6`] | /// | [`TempFile`] | _inherit_ | **no default** | Yes | Yes | Data limits apply. See [`TempFile`]. | -/// | [`Capped`] | _inherit_ | **no default** | Yes | Yes | `C` is `&str`, `String`, or `TempFile`. | +/// | [`Capped`] | _inherit_ | **no default** | Yes | Yes | `C` is `&str`, `String`, `&[u8]` or `TempFile`. | /// | [`time::Date`] | _inherit_ | **no default** | No | Yes | `%F` (`YYYY-MM-DD`). HTML "date" input. | /// | [`time::DateTime`] | _inherit_ | **no default** | No | Yes | `%FT%R` or `%FT%T` (`YYYY-MM-DDTHH:MM[:SS]`) | /// | [`time::Time`] | _inherit_ | **no default** | No | Yes | `%R` or `%T` (`HH:MM[:SS]`) | @@ -628,6 +628,8 @@ impl<'v, T: FromForm<'v> + 'v> FromForm<'v> for Vec { } } +// impl_strict_from_form_field_from_capped!(Vec); + #[doc(hidden)] pub struct MapContext<'v, K, V> where K: FromForm<'v>, V: FromForm<'v> { opts: Options, diff --git a/core/lib/src/form/from_form_field.rs b/core/lib/src/form/from_form_field.rs index 9035ad38ae..d03ee38693 100644 --- a/core/lib/src/form/from_form_field.rs +++ b/core/lib/src/form/from_form_field.rs @@ -28,11 +28,32 @@ use crate::form::prelude::*; /// } /// ``` /// +/// # Semantics +/// +/// The implementation of `FromForm` for a `T: FromFormField` type operates as +/// follows: +/// +/// * When parsing is **strict**, the parser accepts the _first_ value or data +/// field with the corresponding field name and calls `T::from_value()` or +/// `T::from_data()` with the field's value, respectively. If more than one +/// field value is seen, an [`ErrorKind::Duplicate`) is emitted. If no +/// matching field is seen, an [`ErrorKind::Missing`] is emitted. Otherwise, +/// the result from the call is emitted. +/// +/// * When parsing is **lenient**, the parser accepts the first _expected_ +/// value or data field with the corresponding field name and calls +/// `T::from_value()` or `T::from_data()` with the field's value, +/// respectively. Unexpected values, identified by returning an +/// [`ErrorKind::Unexpected`] from `from_value()` or `from_data()` are +/// ignored. Any additional fields with a matching field name are ignored. +/// If no matching field is seen and `T` has a default, it is used, +/// otherwise an [`ErrorKind::Missing`] is emitted. +/// /// # Deriving /// /// `FromFormField` can be derived for C-like enums, where the generated /// implementation case-insensitively parses fields with values equal to the -/// name of the variant or the value in `field(value = "...")`. +/// name of the variant or the value in `field()`. /// /// ```rust /// # use rocket::form::FromFormField; From ae68742048e96d1e60f1d68f28c500793a61a97f Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Mon, 9 Oct 2023 17:03:21 -0700 Subject: [PATCH 08/22] Update dependencies. * notify: 5 -> 6 * indexmap: 1 -> 2 * cookie: 0.18.0-rc.0 -> 0.18 --- contrib/dyn_templates/Cargo.toml | 2 +- core/codegen/Cargo.toml | 2 +- core/http/Cargo.toml | 4 ++-- core/lib/Cargo.toml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/contrib/dyn_templates/Cargo.toml b/contrib/dyn_templates/Cargo.toml index 6a226ac181..166bdf8fd6 100644 --- a/contrib/dyn_templates/Cargo.toml +++ b/contrib/dyn_templates/Cargo.toml @@ -18,7 +18,7 @@ handlebars = ["handlebars_"] [dependencies] glob = "0.3" -notify = "5.0.0" +notify = "6" normpath = "1" [dependencies.rocket] diff --git a/core/codegen/Cargo.toml b/core/codegen/Cargo.toml index 13b59390a4..bda61846b7 100644 --- a/core/codegen/Cargo.toml +++ b/core/codegen/Cargo.toml @@ -16,7 +16,7 @@ rust-version = "1.56" proc-macro = true [dependencies] -indexmap = "1.0" +indexmap = "2" quote = "1.0" syn = { version = "2.0", features = ["full", "visit", "visit-mut", "extra-traits"] } proc-macro2 = "1.0.27" diff --git a/core/http/Cargo.toml b/core/http/Cargo.toml index 41fd3813b8..6e3020ff9b 100644 --- a/core/http/Cargo.toml +++ b/core/http/Cargo.toml @@ -29,7 +29,7 @@ smallvec = "1.0" percent-encoding = "2" http = "0.2" time = { version = "0.3", features = ["formatting", "macros"] } -indexmap = { version = "1.5.2", features = ["std"] } +indexmap = "2" rustls = { version = "0.21", optional = true } tokio-rustls = { version = "0.24", optional = true } rustls-pemfile = { version = "1.0.2", optional = true } @@ -42,7 +42,7 @@ pear = "0.2.3" pin-project-lite = "0.2" memchr = "2" stable-pattern = "0.1" -cookie = { version = "=0.18.0-rc.0", features = ["percent-encode"] } +cookie = { version = "0.18", features = ["percent-encode"] } state = "0.6" futures = { version = "0.3", default-features = false } diff --git a/core/lib/Cargo.toml b/core/lib/Cargo.toml index bed0ec8b76..092e08ff2c 100644 --- a/core/lib/Cargo.toml +++ b/core/lib/Cargo.toml @@ -52,7 +52,7 @@ figment = { version = "0.10.6", features = ["toml", "env"] } rand = "0.8" either = "1" pin-project-lite = "0.2" -indexmap = { version = "1.0", features = ["serde-1", "std"] } +indexmap = { version = "2", features = ["serde"] } tempfile = "3" async-trait = "0.1.43" async-stream = "0.3.2" From f950d3e0ec497281781523444c2e63b2dd38cb27 Mon Sep 17 00:00:00 2001 From: Fenhl Date: Tue, 19 Sep 2023 01:08:15 +0000 Subject: [PATCH 09/22] Upgrade 'tokio-tungstenite' to 0.20. --- contrib/ws/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/ws/Cargo.toml b/contrib/ws/Cargo.toml index 81a8af7b65..10068cca24 100644 --- a/contrib/ws/Cargo.toml +++ b/contrib/ws/Cargo.toml @@ -17,7 +17,7 @@ default = ["tungstenite"] tungstenite = ["tokio-tungstenite"] [dependencies] -tokio-tungstenite = { version = "0.19", optional = true } +tokio-tungstenite = { version = "0.20", optional = true } [dependencies.rocket] version = "=0.5.0-rc.3" From ed5c755bb69991727b284fc3c255d77e0d2fe1da Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Sat, 14 Oct 2023 15:00:36 -0700 Subject: [PATCH 10/22] Avoid using 'glob' to walk templates directory. Previously, `dyn_templates` walked the user-provided `template_dir` path by constructing a glob pattern prefixed with `template_dir`. If `template_dir` contained characters recognized by the glob pattern parser, then at best the pattern failed to parse, and at worst, incorrect directories were searched. This commit removes the use of `glob` to walk the templates directory and instead uses `walkdir`, obviating the issues described above. Fixes #2627. --- contrib/dyn_templates/Cargo.toml | 2 +- contrib/dyn_templates/src/context.rs | 33 ++++++++++++------- contrib/dyn_templates/tests/templates.rs | 10 ++++++ .../templates/tera/[test]/html_test.html.tera | 5 +++ 4 files changed, 38 insertions(+), 12 deletions(-) create mode 100644 contrib/dyn_templates/tests/templates/tera/[test]/html_test.html.tera diff --git a/contrib/dyn_templates/Cargo.toml b/contrib/dyn_templates/Cargo.toml index 166bdf8fd6..d4386d2b2b 100644 --- a/contrib/dyn_templates/Cargo.toml +++ b/contrib/dyn_templates/Cargo.toml @@ -17,7 +17,7 @@ tera = ["tera_"] handlebars = ["handlebars_"] [dependencies] -glob = "0.3" +walkdir = "2.4" notify = "6" normpath = "1" diff --git a/contrib/dyn_templates/src/context.rs b/contrib/dyn_templates/src/context.rs index cf698823d9..ecdd93212a 100644 --- a/contrib/dyn_templates/src/context.rs +++ b/contrib/dyn_templates/src/context.rs @@ -26,6 +26,12 @@ impl Context { /// template engine, and store all of the initialized state in a `Context` /// structure, which is returned if all goes well. pub fn initialize(root: &Path, callback: &Callback) -> Option { + fn is_file_with_ext(entry: &walkdir::DirEntry, ext: &str) -> bool { + let is_file = entry.file_type().is_file(); + let has_ext = entry.path().extension().map_or(false, |e| e == ext); + is_file && has_ext + } + let root = match root.normalize() { Ok(root) => root.into_path_buf(), Err(e) => { @@ -35,18 +41,23 @@ impl Context { }; let mut templates: HashMap = HashMap::new(); - for ext in Engines::ENABLED_EXTENSIONS { - let mut glob_path = root.join("**").join("*"); - glob_path.set_extension(ext); - let glob_path = glob_path.to_str().expect("valid glob path string"); + for &ext in Engines::ENABLED_EXTENSIONS { + for entry in walkdir::WalkDir::new(&root).follow_links(true) { + let entry = match entry { + Ok(entry) if is_file_with_ext(&entry, ext) => entry, + Ok(_) | Err(_) => continue, + }; - for path in glob::glob(glob_path).unwrap().filter_map(Result::ok) { - let (name, data_type_str) = split_path(&root, &path); + let (name, data_type_str) = split_path(&root, entry.path()); if let Some(info) = templates.get(&*name) { - warn_!("Template name '{}' does not have a unique path.", name); - info_!("Existing path: {:?}", info.path); - info_!("Additional path: {:?}", path); - warn_!("Using existing path for template '{}'.", name); + warn_!("Template name '{}' does not have a unique source.", name); + match info.path { + Some(ref path) => info_!("Existing path: {:?}", path), + None => info_!("Existing Content-Type: {}", info.data_type), + } + + info_!("Additional path: {:?}", entry.path()); + warn_!("Keeping existing template '{}'.", name); continue; } @@ -55,7 +66,7 @@ impl Context { .unwrap_or(ContentType::Text); templates.insert(name, TemplateInfo { - path: Some(path.clone()), + path: Some(entry.into_path()), engine_ext: ext, data_type, }); diff --git a/contrib/dyn_templates/tests/templates.rs b/contrib/dyn_templates/tests/templates.rs index c73927f94a..8db5923df9 100644 --- a/contrib/dyn_templates/tests/templates.rs +++ b/contrib/dyn_templates/tests/templates.rs @@ -237,6 +237,16 @@ mod tera_tests { assert_eq!(md_rendered, Some((ContentType::HTML, ESCAPED_EXPECTED.into()))); } + #[async_test] + async fn test_globby_paths() { + use rocket::local::asynchronous::Client; + + let client = Client::debug(rocket()).await.unwrap(); + let req = client.get("/"); + let metadata = Metadata::from_request(&req).await.unwrap(); + assert!(metadata.contains_template("tera/[test]/html_test")); + } + // u128 is not supported. enable when it is. // #[test] // fn test_tera_u128() { diff --git a/contrib/dyn_templates/tests/templates/tera/[test]/html_test.html.tera b/contrib/dyn_templates/tests/templates/tera/[test]/html_test.html.tera new file mode 100644 index 0000000000..17cd1b2023 --- /dev/null +++ b/contrib/dyn_templates/tests/templates/tera/[test]/html_test.html.tera @@ -0,0 +1,5 @@ +{% extends "tera/base" %} +{% block title %}{{ title }}{% endblock title %} +{% block content %} +{{ content }} +{% endblock content %} From 58adc489651cfa9f2e8fedf2feab9e7cd07e2bb7 Mon Sep 17 00:00:00 2001 From: Ruben Schmidmeister <4602612+bash@users.noreply.github.com> Date: Wed, 23 Aug 2023 22:49:27 +0200 Subject: [PATCH 11/22] Set 'color-scheme' in default catcher HTML. --- core/lib/src/catcher/catcher.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/core/lib/src/catcher/catcher.rs b/core/lib/src/catcher/catcher.rs index faa54758f3..3ed8ff327d 100644 --- a/core/lib/src/catcher/catcher.rs +++ b/core/lib/src/catcher/catcher.rs @@ -373,6 +373,7 @@ r#" + "#, $code, " ", $reason, r#" From ac01e55e8bf732c6826cea3224c79f0456062a0c Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Mon, 14 Aug 2023 15:46:21 -0700 Subject: [PATCH 12/22] Fix typo in 'rocket_ws' README. --- contrib/ws/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/ws/README.md b/contrib/ws/README.md index bd889e8950..825bdab6d6 100644 --- a/contrib/ws/README.md +++ b/contrib/ws/README.md @@ -24,7 +24,7 @@ This crate provides WebSocket support for Rocket via integration with Rocket's ```rust #[get("/echo")] fn echo_stream(ws: ws::WebSocket) -> ws::Stream!['static] { - ws::stream! { ws => + ws::Stream! { ws => for await message in ws { yield message?; } From 07fe79796f058ab12683ff9e344558bece263274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= Date: Sun, 30 Jul 2023 21:32:05 +0800 Subject: [PATCH 13/22] Use Span::mixed_site to avoid let unit warnings. Closes #2568. --- core/codegen/src/attribute/route/mod.rs | 2 +- .../tests/ui-fail-nightly/async-entry.stderr | 7 +++---- .../tests/ui-fail-nightly/responder-types.stderr | 3 +++ .../tests/ui-fail-stable/async-entry.stderr | 16 ++++++++-------- core/codegen/tests/ui-fail-stable/catch.stderr | 2 +- .../tests/ui-fail-stable/responder-types.stderr | 3 +++ .../ui-fail-stable/route-type-errors.stderr | 2 +- 7 files changed, 20 insertions(+), 15 deletions(-) diff --git a/core/codegen/src/attribute/route/mod.rs b/core/codegen/src/attribute/route/mod.rs index 8003dda430..1079f3066b 100644 --- a/core/codegen/src/attribute/route/mod.rs +++ b/core/codegen/src/attribute/route/mod.rs @@ -242,7 +242,7 @@ fn responder_outcome_expr(route: &Route) -> TokenStream { .map(|a| quote_spanned!(a.span() => .await)); define_spanned_export!(ret_span => __req, _route); - quote_spanned! { ret_span => + quote_spanned! { Span::mixed_site().located_at(ret_span) => let ___responder = #user_handler_fn_name(#(#parameter_names),*) #_await; #_route::Outcome::from(#__req, ___responder) } diff --git a/core/codegen/tests/ui-fail-nightly/async-entry.stderr b/core/codegen/tests/ui-fail-nightly/async-entry.stderr index 74374c654a..f6acb1576b 100644 --- a/core/codegen/tests/ui-fail-nightly/async-entry.stderr +++ b/core/codegen/tests/ui-fail-nightly/async-entry.stderr @@ -149,10 +149,9 @@ error[E0308]: mismatched types --> tests/ui-fail-nightly/async-entry.rs:24:21 | 24 | async fn main() { - | ^ - | | - | _____________________expected `()` because of default return type - | | + | ____________________-^ + | | | + | | expected `()` because of default return type 25 | | rocket::build() 26 | | } | | ^- help: consider using a semicolon here: `;` diff --git a/core/codegen/tests/ui-fail-nightly/responder-types.stderr b/core/codegen/tests/ui-fail-nightly/responder-types.stderr index 58956ef88f..c54893bf41 100644 --- a/core/codegen/tests/ui-fail-nightly/responder-types.stderr +++ b/core/codegen/tests/ui-fail-nightly/responder-types.stderr @@ -104,6 +104,8 @@ note: required by a bound in `rocket::Response::<'r>::set_header` error[E0277]: the trait bound `usize: Responder<'_, '_>` is not satisfied --> tests/ui-fail-nightly/responder-types.rs:28:13 | +27 | #[get("/")] + | ----------- in this procedural macro expansion 28 | fn foo() -> usize { 0 } | ^^^^^ the trait `Responder<'_, '_>` is not implemented for `usize` | @@ -122,3 +124,4 @@ note: required by a bound in `route::handler:: | | pub fn from>(req: &'r Request<'_>, responder: R) -> Outcome<'r> { | ^^^^^^^^^^^^^^^^^ required by this bound in `route::handler::, Status, (Data<'o>, Status)>>::from` + = note: this error originates in the attribute macro `get` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/core/codegen/tests/ui-fail-stable/async-entry.stderr b/core/codegen/tests/ui-fail-stable/async-entry.stderr index dbd9512c43..d3ac4cc7af 100644 --- a/core/codegen/tests/ui-fail-stable/async-entry.stderr +++ b/core/codegen/tests/ui-fail-stable/async-entry.stderr @@ -107,6 +107,14 @@ error[E0728]: `await` is only allowed inside `async` functions and blocks 73 | let _ = rocket::build().launch().await; | ^^^^^ only allowed inside `async` functions and blocks +error[E0277]: `main` has invalid return type `Rocket` + --> tests/ui-fail-stable/async-entry.rs:94:20 + | +94 | async fn main() -> rocket::Rocket { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `main` can only return types that implement `Termination` + | + = help: consider using `()`, or a `Result` + error[E0308]: mismatched types --> tests/ui-fail-stable/async-entry.rs:35:9 | @@ -165,11 +173,3 @@ error[E0308]: mismatched types | = note: expected struct `Rocket` found struct `std::string::String` - -error[E0277]: `main` has invalid return type `Rocket` - --> tests/ui-fail-stable/async-entry.rs:94:20 - | -94 | async fn main() -> rocket::Rocket { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `main` can only return types that implement `Termination` - | - = help: consider using `()`, or a `Result` diff --git a/core/codegen/tests/ui-fail-stable/catch.stderr b/core/codegen/tests/ui-fail-stable/catch.stderr index 49ded6be31..fdb47184a9 100644 --- a/core/codegen/tests/ui-fail-stable/catch.stderr +++ b/core/codegen/tests/ui-fail-stable/catch.stderr @@ -67,7 +67,7 @@ note: function defined here | 30 | fn f3(_request: &Request, other: bool) { } | ^^ ------------------ ----------- -help: did you mean +help: provide the argument | 29 | f3(bool, /* bool */) | diff --git a/core/codegen/tests/ui-fail-stable/responder-types.stderr b/core/codegen/tests/ui-fail-stable/responder-types.stderr index 1ee3af24c2..2526127ea7 100644 --- a/core/codegen/tests/ui-fail-stable/responder-types.stderr +++ b/core/codegen/tests/ui-fail-stable/responder-types.stderr @@ -104,6 +104,8 @@ note: required by a bound in `rocket::Response::<'r>::set_header` error[E0277]: the trait bound `usize: Responder<'_, '_>` is not satisfied --> tests/ui-fail-stable/responder-types.rs:28:13 | +27 | #[get("/")] + | ----------- in this procedural macro expansion 28 | fn foo() -> usize { 0 } | ^^^^^ the trait `Responder<'_, '_>` is not implemented for `usize` | @@ -122,3 +124,4 @@ note: required by a bound in `route::handler:: | | pub fn from>(req: &'r Request<'_>, responder: R) -> Outcome<'r> { | ^^^^^^^^^^^^^^^^^ required by this bound in `route::handler::, Status, (Data<'o>, Status)>>::from` + = note: this error originates in the attribute macro `get` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/core/codegen/tests/ui-fail-stable/route-type-errors.stderr b/core/codegen/tests/ui-fail-stable/route-type-errors.stderr index 40dd0d27cd..dbde5a8882 100644 --- a/core/codegen/tests/ui-fail-stable/route-type-errors.stderr +++ b/core/codegen/tests/ui-fail-stable/route-type-errors.stderr @@ -74,8 +74,8 @@ error[E0277]: the trait bound `Q: FromData<'_>` is not satisfied Cow<'_, str> Capped> Capped> - Capped Capped> + Capped Capped<&'r str> Capped<&'r RawStr> and $N others From 260e671d43bfc6345561c40f377d6d70efc1d99e Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Fri, 27 Oct 2023 14:50:48 -0500 Subject: [PATCH 14/22] Use mixed-site spans to avoid clippy 'uri!' error. Closes #2630. --- core/codegen/src/attribute/route/mod.rs | 3 ++- core/codegen/src/bang/uri.rs | 2 +- core/codegen/src/exports.rs | 6 ++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/core/codegen/src/attribute/route/mod.rs b/core/codegen/src/attribute/route/mod.rs index 1079f3066b..93fac9b9de 100644 --- a/core/codegen/src/attribute/route/mod.rs +++ b/core/codegen/src/attribute/route/mod.rs @@ -10,6 +10,7 @@ use crate::proc_macro_ext::StringLit; use crate::syn_ext::{IdentExt, TypeExt as _}; use crate::http_codegen::{Method, Optional}; use crate::attribute::param::Guard; +use crate::exports::mixed; use self::parse::{Route, Attribute, MethodAttribute}; @@ -242,7 +243,7 @@ fn responder_outcome_expr(route: &Route) -> TokenStream { .map(|a| quote_spanned!(a.span() => .await)); define_spanned_export!(ret_span => __req, _route); - quote_spanned! { Span::mixed_site().located_at(ret_span) => + quote_spanned! { mixed(ret_span) => let ___responder = #user_handler_fn_name(#(#parameter_names),*) #_await; #_route::Outcome::from(#__req, ___responder) } diff --git a/core/codegen/src/bang/uri.rs b/core/codegen/src/bang/uri.rs index 175b73da7e..b8e6c1d42b 100644 --- a/core/codegen/src/bang/uri.rs +++ b/core/codegen/src/bang/uri.rs @@ -126,7 +126,7 @@ fn add_binding(to: &mut Vec, ident: &Ident, ty: &Type let tmp_ident = ident.clone().with_span(expr.span()); let let_stmt = quote_spanned!(span => let #tmp_ident = #expr); - to.push(quote_spanned!(span => + to.push(quote_spanned!(mixed(span) => #[allow(non_snake_case)] #let_stmt; let #ident = <#ty as #_fmt::FromUriParam<#part, _>>::from_uri_param(#tmp_ident); )); diff --git a/core/codegen/src/exports.rs b/core/codegen/src/exports.rs index d6c1f4d911..1c947ac355 100644 --- a/core/codegen/src/exports.rs +++ b/core/codegen/src/exports.rs @@ -106,3 +106,9 @@ define_exported_paths! { macro_rules! define_spanned_export { ($span:expr => $($name:ident),*) => ($(define!($span => $name $name);)*) } + +/// Convenience: returns a "mixed site" span located at `span`. +#[inline(always)] +pub fn mixed(span: Span) -> Span { + Span::mixed_site().located_at(span) +} From 11c9c3cbcd2ce66501f0fe44cdb0964ad4ef78a7 Mon Sep 17 00:00:00 2001 From: Steven Murdoch Date: Wed, 14 Jun 2023 13:51:18 +0200 Subject: [PATCH 15/22] Fix off-by-one, improve style in forms example. --- examples/forms/templates/index.html.tera | 17 +++++++++++++---- examples/forms/templates/macros.html.tera | 10 +++++----- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/examples/forms/templates/index.html.tera b/examples/forms/templates/index.html.tera index 78f06469bf..dd227b8e3a 100644 --- a/examples/forms/templates/index.html.tera +++ b/examples/forms/templates/index.html.tera @@ -13,16 +13,25 @@ margin: 0 auto; padding: 20px 10px; } + + h1 { + margin: 10px 0; + }

Form Example

- {% if errors | length > 1 %} - - {{ errors | length }} field(s) have errors - + {% if errors | length > 0 %} +
+
+ + error: {{ errors | length }} field{{ errors | length | pluralize }} + failed to validate + +
+
{% endif %}
diff --git a/examples/forms/templates/macros.html.tera b/examples/forms/templates/macros.html.tera index 2b0128c307..ec7144ebc6 100644 --- a/examples/forms/templates/macros.html.tera +++ b/examples/forms/templates/macros.html.tera @@ -2,7 +2,7 @@ {%- if name in values -%} {{- values | get(key=name) | first -}} {%- endif -%} -{% endmacro %} +{% endmacro value_for %} {% macro errors_for(name) %} {%- if name in errors -%} @@ -11,7 +11,7 @@

{{ error.msg }}

{% endfor %} {%- endif -%} -{% endmacro %} +{% endmacro errors_for %} {% macro input(type, label, name, value="") %} @@ -37,7 +37,7 @@ > {{ label }} -{% endmacro input %} +{% endmacro checkbox %} {% macro textarea(label, name, placeholder="", max=250) %} @@ -49,7 +49,7 @@ {{ self::errors_for(name=name) }} -{% endmacro input %} +{% endmacro textarea %} {% macro select(label, name, options) %} @@ -60,4 +60,4 @@ >{{ value }} {% endfor %} -{% endmacro input %} +{% endmacro select %} From c90812051e21f25eb54649eb0c13f6793de4b50b Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Tue, 31 Oct 2023 12:11:03 -0500 Subject: [PATCH 16/22] Rename 'Outcome::Failure' to 'Outcome::Error'. The primary motivation is to deconflate the leading `F`s in `Failure` and `Forward`. In particular, when using a generics, we used `F` for forward, which could easily be confused for `F` for `Failure`. This resolves the conflation. --- contrib/db_pools/codegen/src/database.rs | 2 +- contrib/db_pools/lib/src/database.rs | 6 +- contrib/dyn_templates/src/metadata.rs | 2 +- contrib/sync_db_pools/lib/src/connection.rs | 2 +- core/codegen/src/attribute/route/mod.rs | 8 +- core/codegen/src/lib.rs | 7 +- core/http/src/parse/uri/tests.rs | 4 +- core/http/src/tls/mtls.rs | 2 +- core/lib/src/catcher/catcher.rs | 12 +- core/lib/src/config/config.rs | 2 +- core/lib/src/config/secret_key.rs | 4 +- core/lib/src/data/capped.rs | 4 +- core/lib/src/data/from_data.rs | 24 +-- core/lib/src/error.rs | 4 +- core/lib/src/fairing/mod.rs | 6 +- core/lib/src/form/error.rs | 2 +- core/lib/src/form/form.rs | 6 +- core/lib/src/form/from_form_field.rs | 6 +- core/lib/src/form/parser.rs | 2 +- core/lib/src/form/validate.rs | 20 +-- core/lib/src/fs/server.rs | 4 +- core/lib/src/outcome.rs | 165 ++++++++++---------- core/lib/src/request/from_request.rs | 75 ++++----- core/lib/src/response/responder.rs | 2 +- core/lib/src/route/handler.rs | 20 +-- core/lib/src/route/route.rs | 2 +- core/lib/src/serde/json.rs | 6 +- core/lib/src/serde/msgpack.rs | 6 +- core/lib/src/serde/uuid.rs | 2 +- core/lib/src/server.rs | 14 +- core/lib/src/state.rs | 2 +- examples/manual-routing/src/main.rs | 6 +- site/guide/4-requests.md | 10 +- site/guide/5-responses.md | 2 +- site/tests/src/lib.rs | 2 +- 35 files changed, 222 insertions(+), 221 deletions(-) diff --git a/contrib/db_pools/codegen/src/database.rs b/contrib/db_pools/codegen/src/database.rs index 837aa6c8ac..a2ca218de1 100644 --- a/contrib/db_pools/codegen/src/database.rs +++ b/contrib/db_pools/codegen/src/database.rs @@ -67,7 +67,7 @@ pub fn derive_database(input: TokenStream) -> TokenStream { ) -> rocket::request::Outcome { match #db_ty::fetch(req.rocket()) { Some(db) => rocket::outcome::Outcome::Success(db), - None => rocket::outcome::Outcome::Failure(( + None => rocket::outcome::Outcome::Error(( rocket::http::Status::InternalServerError, ())) } } diff --git a/contrib/db_pools/lib/src/database.rs b/contrib/db_pools/lib/src/database.rs index 4afbd1f3e4..b8179be052 100644 --- a/contrib/db_pools/lib/src/database.rs +++ b/contrib/db_pools/lib/src/database.rs @@ -164,7 +164,7 @@ pub struct Initializer(Option<&'static str>, PhantomData D> /// [`connect_timeout`](crate::Config::connect_timeout) seconds. /// * If the `Initializer` fairing was _not_ attached, the guard _fails_ with /// status `InternalServerError`. A [`Sentinel`] guards this condition, and so -/// this type of failure is unlikely to occur. A `None` error is returned. +/// this type of error is unlikely to occur. A `None` error is returned. /// * If a connection is not available within `connect_timeout` seconds or /// another error occurs, the guard _fails_ with status `ServiceUnavailable` /// and the error is returned in `Some`. @@ -288,9 +288,9 @@ impl<'r, D: Database> FromRequest<'r> for Connection { match D::fetch(req.rocket()) { Some(db) => match db.get().await { Ok(conn) => Outcome::Success(Connection(conn)), - Err(e) => Outcome::Failure((Status::ServiceUnavailable, Some(e))), + Err(e) => Outcome::Error((Status::ServiceUnavailable, Some(e))), }, - None => Outcome::Failure((Status::InternalServerError, None)), + None => Outcome::Error((Status::InternalServerError, None)), } } } diff --git a/contrib/dyn_templates/src/metadata.rs b/contrib/dyn_templates/src/metadata.rs index 06d722d2d1..9f52f33dfb 100644 --- a/contrib/dyn_templates/src/metadata.rs +++ b/contrib/dyn_templates/src/metadata.rs @@ -153,7 +153,7 @@ impl<'r> FromRequest<'r> for Metadata<'r> { error_!("Uninitialized template context: missing fairing."); info_!("To use templates, you must attach `Template::fairing()`."); info_!("See the `Template` documentation for more information."); - request::Outcome::Failure((Status::InternalServerError, ())) + request::Outcome::Error((Status::InternalServerError, ())) }) } } diff --git a/contrib/sync_db_pools/lib/src/connection.rs b/contrib/sync_db_pools/lib/src/connection.rs index a52aae72ff..c395dc2866 100644 --- a/contrib/sync_db_pools/lib/src/connection.rs +++ b/contrib/sync_db_pools/lib/src/connection.rs @@ -213,7 +213,7 @@ impl<'r, K: 'static, C: Poolable> FromRequest<'r> for Connection { Some(c) => c.get().await.into_outcome((Status::ServiceUnavailable, ())), None => { error_!("Missing database fairing for `{}`", std::any::type_name::()); - Outcome::Failure((Status::InternalServerError, ())) + Outcome::Error((Status::InternalServerError, ())) } } } diff --git a/core/codegen/src/attribute/route/mod.rs b/core/codegen/src/attribute/route/mod.rs index 93fac9b9de..e30117db00 100644 --- a/core/codegen/src/attribute/route/mod.rs +++ b/core/codegen/src/attribute/route/mod.rs @@ -126,9 +126,9 @@ fn request_guard_decl(guard: &Guard) -> TokenStream { #_log::warn_!("Request guard `{}` is forwarding.", stringify!(#ty)); return #Outcome::Forward((#__data, __e)); }, - #Outcome::Failure((__c, __e)) => { + #Outcome::Error((__c, __e)) => { #_log::warn_!("Request guard `{}` failed: {:?}.", stringify!(#ty), __e); - return #Outcome::Failure(__c); + return #Outcome::Error(__c); } }; } @@ -189,9 +189,9 @@ fn data_guard_decl(guard: &Guard) -> TokenStream { #_log::warn_!("Data guard `{}` is forwarding.", stringify!(#ty)); return #Outcome::Forward((__d, __e)); } - #Outcome::Failure((__c, __e)) => { + #Outcome::Error((__c, __e)) => { #_log::warn_!("Data guard `{}` failed: {:?}.", stringify!(#ty), __e); - return #Outcome::Failure(__c); + return #Outcome::Error(__c); } }; } diff --git a/core/codegen/src/lib.rs b/core/codegen/src/lib.rs index 7cec0eb073..abcf387262 100644 --- a/core/codegen/src/lib.rs +++ b/core/codegen/src/lib.rs @@ -239,8 +239,7 @@ macro_rules! route_attribute { /// /// If a request guard fails, the request is forwarded if the /// [`Outcome`] is `Forward` or failed if the [`Outcome`] is - /// `Failure`. See [`FromRequest` Outcomes] for further - /// detail. + /// `Error`. See [`FromRequest` Outcomes] for further detail. /// /// 2. Path and query guards in an unspecified order. If a path /// or query guard fails, the request is forwarded. @@ -249,7 +248,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`] for further detail. + /// `Error`. See [`FromData`] for further detail. /// /// If all validation succeeds, the decorated function is called. /// The returned value is used to generate a [`Response`] via the @@ -448,7 +447,7 @@ pub fn main(args: TokenStream, input: TokenStream) -> TokenStream { /// #[rocket::main] /// async fn main() { /// // Recall that an uninspected `Error` will cause a pretty-printed panic, -/// // so rest assured failures do not go undetected when using `#[launch]`. +/// // so rest assured errors do not go undetected when using `#[launch]`. /// let _ = rocket().launch().await; /// } /// ``` diff --git a/core/http/src/parse/uri/tests.rs b/core/http/src/parse/uri/tests.rs index 7106e4b2f0..ac7b506c9e 100644 --- a/core/http/src/parse/uri/tests.rs +++ b/core/http/src/parse/uri/tests.rs @@ -8,7 +8,7 @@ macro_rules! assert_parse_eq { match from_str($from) { Ok(output) => { if output != expected { - println!("Failure on: {:?}", $from); + println!("Error on: {:?}", $from); assert_eq!(output, expected, "{} != {}", output, expected); } } @@ -53,7 +53,7 @@ macro_rules! assert_displays_eq { Ok(output) => { let output_string = output.to_string(); if output_string != string { - println!("Failure on: {:?}", $string); + println!("Error on: {:?}", $string); println!("Got: {:?}", output_string); println!("Parsed as: {:?}", output); panic!("failed"); diff --git a/core/http/src/tls/mtls.rs b/core/http/src/tls/mtls.rs index 9d7554fb7f..45ea38fc48 100644 --- a/core/http/src/tls/mtls.rs +++ b/core/http/src/tls/mtls.rs @@ -533,7 +533,7 @@ impl fmt::Display for Name<'_> { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Error::Parse(e) => write!(f, "parse failure: {}", e), + Error::Parse(e) => write!(f, "parse error: {}", e), Error::Incomplete(_) => write!(f, "incomplete certificate data"), Error::Trailing(n) => write!(f, "found {} trailing bytes", n), Error::Empty => write!(f, "empty certificate chain"), diff --git a/core/lib/src/catcher/catcher.rs b/core/lib/src/catcher/catcher.rs index 3ed8ff327d..299d7b08c8 100644 --- a/core/lib/src/catcher/catcher.rs +++ b/core/lib/src/catcher/catcher.rs @@ -18,12 +18,14 @@ use yansi::Paint; /// /// * A failing guard. /// * A failing responder. +/// * A forwarding guard. /// * Routing failure. /// -/// Each failure is paired with a status code. Guards and responders indicate -/// the status code themselves via their `Err` return value while a routing -/// failure is always a `404`. Rocket invokes the error handler for the catcher -/// with the error's status code. +/// Each error or forward is paired with a status code. Guards and responders +/// indicate the status code themselves via their `Err` and `Outcome` return +/// value. A complete routing failure is always a `404`. Rocket invokes the +/// error handler for the catcher with an error's status code, or in the case of +/// every route resulting in a forward, the last forwarded status code. /// /// ### Error Handler Restrictions /// @@ -33,7 +35,7 @@ use yansi::Paint; /// /// # Routing /// -/// If a route fails by returning a failure [`Outcome`], Rocket routes the +/// If a route fails by returning an error [`Outcome`], Rocket routes the /// erroring request to the highest precedence catcher among all the catchers /// that [match](Catcher::matches()). See [`Catcher::matches()`] for details on /// matching. Precedence is determined by the catcher's _base_, which is diff --git a/core/lib/src/config/config.rs b/core/lib/src/config/config.rs index 61b25bffa7..4a23bf4f3d 100644 --- a/core/lib/src/config/config.rs +++ b/core/lib/src/config/config.rs @@ -288,7 +288,7 @@ impl Config { /// /// # Panics /// - /// If extraction fails, prints an error message indicating the failure and + /// If extraction fails, prints an error message indicating the error and /// panics. For a version that doesn't panic, use [`Config::try_from()`]. /// /// # Example diff --git a/core/lib/src/config/secret_key.rs b/core/lib/src/config/secret_key.rs index 445f960156..02947e7090 100644 --- a/core/lib/src/config/secret_key.rs +++ b/core/lib/src/config/secret_key.rs @@ -52,7 +52,7 @@ enum Kind { /// ``` /// /// When running in any other profile with the `secrets` feature enabled, -/// providing a key of `0` or not provided a key at all results in a failure at +/// providing a key of `0` or not provided a key at all results in an error at /// launch-time: /// /// ```rust @@ -67,7 +67,7 @@ enum Kind { /// .select(profile.clone()); /// /// let rocket = rocket::custom(figment); -/// let error = Client::tracked(rocket).expect_err("failure in non-debug"); +/// let error = Client::tracked(rocket).expect_err("error in non-debug"); /// assert!(matches!(error.kind(), ErrorKind::InsecureSecretKey(profile))); /// ``` /// diff --git a/core/lib/src/data/capped.rs b/core/lib/src/data/capped.rs index 335c980f1b..804a42d486 100644 --- a/core/lib/src/data/capped.rs +++ b/core/lib/src/data/capped.rs @@ -260,10 +260,10 @@ macro_rules! impl_strict_from_data_from_capped { Success(p) if p.is_complete() => Success(p.into_inner()), Success(_) => { let e = Error::new(UnexpectedEof, "data limit exceeded"); - Failure((Status::BadRequest, e.into())) + Error((Status::BadRequest, e.into())) }, Forward(d) => Forward(d), - Failure((s, e)) => Failure((s, e)), + Error((s, e)) => Error((s, e)), } } } diff --git a/core/lib/src/data/from_data.rs b/core/lib/src/data/from_data.rs index 2b7465c351..b3ac98c816 100644 --- a/core/lib/src/data/from_data.rs +++ b/core/lib/src/data/from_data.rs @@ -10,14 +10,14 @@ pub type Outcome<'r, T, E = >::Error> = outcome::Outcome, Status)>; impl<'r, S, E> IntoOutcome, Status)> for Result { - type Failure = Status; + type Error = Status; type Forward = (Data<'r>, Status); #[inline] fn into_outcome(self, status: Status) -> Outcome<'r, S, E> { match self { Ok(val) => Success(val), - Err(err) => Failure((status, err)) + Err(err) => Error((status, err)) } } @@ -251,6 +251,7 @@ impl<'r, S, E> IntoOutcome, Status)> for Result /// use rocket::request::{self, Request}; /// use rocket::data::{self, Data, FromData, ToByteUnit}; /// use rocket::http::{Status, ContentType}; +/// use rocket::outcome::Outcome; /// /// #[derive(Debug)] /// enum Error { @@ -266,12 +267,11 @@ impl<'r, S, E> IntoOutcome, Status)> for Result /// /// async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> data::Outcome<'r, Self> { /// use Error::*; -/// use rocket::outcome::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 Forward((data, Status::NotFound)); +/// return Outcome::Forward((data, Status::NotFound)); /// } /// /// // Use a configured limit with name 'person' or fallback to default. @@ -280,8 +280,8 @@ impl<'r, S, E> IntoOutcome, Status)> for Result /// // Read the data into a string. /// let string = match data.open(limit).into_string().await { /// Ok(string) if string.is_complete() => string.into_inner(), -/// Ok(_) => return Failure((Status::PayloadTooLarge, TooLarge)), -/// Err(e) => return Failure((Status::InternalServerError, Io(e))), +/// Ok(_) => return Outcome::Error((Status::PayloadTooLarge, TooLarge)), +/// Err(e) => return Outcome::Error((Status::InternalServerError, Io(e))), /// }; /// /// // We store `string` in request-local cache for long-lived borrows. @@ -290,16 +290,16 @@ impl<'r, S, E> IntoOutcome, Status)> for Result /// // Split the string into two pieces at ':'. /// let (name, age) = match string.find(':') { /// Some(i) => (&string[..i], &string[(i + 1)..]), -/// None => return Failure((Status::UnprocessableEntity, NoColon)), +/// None => return Outcome::Error((Status::UnprocessableEntity, NoColon)), /// }; /// /// // Parse the age. /// let age: u16 = match age.parse() { /// Ok(age) => age, -/// Err(_) => return Failure((Status::UnprocessableEntity, InvalidAge)), +/// Err(_) => return Outcome::Error((Status::UnprocessableEntity, InvalidAge)), /// }; /// -/// Success(Person { name, age }) +/// Outcome::Success(Person { name, age }) /// } /// } /// @@ -331,7 +331,7 @@ pub trait FromData<'r>: 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. + /// returned. If parsing fails, `Error` is returned. async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self>; } @@ -428,7 +428,7 @@ impl<'r, T: FromData<'r> + 'r> FromData<'r> for Result { async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> { match T::from_data(req, data).await { Success(v) => Success(Ok(v)), - Failure((_, e)) => Success(Err(e)), + Error((_, e)) => Success(Err(e)), Forward(d) => Forward(d), } } @@ -441,7 +441,7 @@ impl<'r, T: FromData<'r>> FromData<'r> for Option { async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> { match T::from_data(req, data).await { Success(v) => Success(Some(v)), - Failure(..) | Forward(..) => Success(None), + Error(..) | Forward(..) => Success(None), } } } diff --git a/core/lib/src/error.rs b/core/lib/src/error.rs index b68b255bcb..28e71fc216 100644 --- a/core/lib/src/error.rs +++ b/core/lib/src/error.rs @@ -216,9 +216,9 @@ impl Error { crate::config::pretty_print_error(error.clone()); "aborting due to invalid configuration" } - ErrorKind::SentinelAborts(ref failures) => { + ErrorKind::SentinelAborts(ref errors) => { error!("Rocket failed to launch due to aborting sentinels:"); - for sentry in failures { + for sentry in errors { let name = sentry.type_name.primary().bold(); let (file, line, col) = sentry.location; info_!("{} ({}:{}:{})", name, file, line, col); diff --git a/core/lib/src/fairing/mod.rs b/core/lib/src/fairing/mod.rs index 642b80b884..1439d8f4cd 100644 --- a/core/lib/src/fairing/mod.rs +++ b/core/lib/src/fairing/mod.rs @@ -66,7 +66,7 @@ pub type Result, E = Rocket> = std::result::Result, E = Rocket> = std::result::ResultLiftoff (`on_liftoff`)** /// @@ -417,7 +417,7 @@ pub type Result, E = Rocket> = std::result::Result) -> request::Outcome { /// match *request.local_cache(|| TimerStart(None)) { /// TimerStart(Some(time)) => request::Outcome::Success(StartTime(time)), -/// TimerStart(None) => request::Outcome::Failure((Status::InternalServerError, ())), +/// TimerStart(None) => request::Outcome::Error((Status::InternalServerError, ())), /// } /// } /// } diff --git a/core/lib/src/form/error.rs b/core/lib/src/form/error.rs index 7167eb5e7b..b6f3c50690 100644 --- a/core/lib/src/form/error.rs +++ b/core/lib/src/form/error.rs @@ -350,7 +350,7 @@ impl<'v> Errors<'v> { /// Returns the highest [`Error::status()`] of all of the errors in `self` /// or [`Status::InternalServerError`] if `self` is empty. This is the /// status that is set by the [`Form`](crate::form::Form) data guard on - /// failure. + /// error. /// /// See [`Error::status()`] for the corresponding status code of each /// [`Error`] variant. diff --git a/core/lib/src/form/form.rs b/core/lib/src/form/form.rs index c747adc2a1..12b1c40ced 100644 --- a/core/lib/src/form/form.rs +++ b/core/lib/src/form/form.rs @@ -65,8 +65,8 @@ use crate::form::prelude::*; /// /// If the request `ContentType` _does_ identify as a form but the form data /// does not parse as `T`, according to `T`'s [`FromForm`] implementation, the -/// guard **fails**. The `Failure` variant contains of the [`Errors`] emitted by -/// `T`'s `FromForm` parser. If the error is not caught by a +/// guard **fails**. The `Error` variant contains a vector of the [`Errors`] +/// emitted by `T`'s `FromForm` parser. If the error is not caught by a /// [`form::Result`](Result) or `Option>` data guard, the status code /// is set to [`Errors::status()`], and the corresponding error catcher is /// called. @@ -334,7 +334,7 @@ impl<'r, T: FromForm<'r>> FromData<'r> for Form { match T::finalize(context) { Ok(value) => Outcome::Success(Form(value)), - Err(e) => Outcome::Failure((e.status(), e)), + Err(e) => Outcome::Error((e.status(), e)), } } } diff --git a/core/lib/src/form/from_form_field.rs b/core/lib/src/form/from_form_field.rs index d03ee38693..7e733133cf 100644 --- a/core/lib/src/form/from_form_field.rs +++ b/core/lib/src/form/from_form_field.rs @@ -297,7 +297,7 @@ impl<'v> FromFormField<'v> for Capped<&'v str> { match as FromData>::from_data(f.request, f.data).await { Outcome::Success(p) => Ok(p), - Outcome::Failure((_, e)) => Err(e)?, + Outcome::Error((_, e)) => Err(e)?, Outcome::Forward(..) => { Err(Error::from(ErrorKind::Unexpected).with_entity(Entity::DataField))? } @@ -318,7 +318,7 @@ impl<'v> FromFormField<'v> for Capped { match as FromData>::from_data(f.request, f.data).await { Outcome::Success(p) => Ok(p), - Outcome::Failure((_, e)) => Err(e)?, + Outcome::Error((_, e)) => Err(e)?, Outcome::Forward(..) => { Err(Error::from(ErrorKind::Unexpected).with_entity(Entity::DataField))? } @@ -354,7 +354,7 @@ impl<'v> FromFormField<'v> for Capped<&'v [u8]> { match as FromData>::from_data(f.request, f.data).await { Outcome::Success(p) => Ok(p), - Outcome::Failure((_, e)) => Err(e)?, + Outcome::Error((_, e)) => Err(e)?, Outcome::Forward(..) => { Err(Error::from(ErrorKind::Unexpected).with_entity(Entity::DataField))? } diff --git a/core/lib/src/form/parser.rs b/core/lib/src/form/parser.rs index 89c42eb888..7a9c4437be 100644 --- a/core/lib/src/form/parser.rs +++ b/core/lib/src/form/parser.rs @@ -40,7 +40,7 @@ impl<'r, 'i> Parser<'r, 'i> { match parser { Ok(storage) => Outcome::Success(storage), - Err(e) => Outcome::Failure((e.status(), e.into())) + Err(e) => Outcome::Error((e.status(), e.into())) } } diff --git a/core/lib/src/form/validate.rs b/core/lib/src/form/validate.rs index 821589cbf5..6a079be0ac 100644 --- a/core/lib/src/form/validate.rs +++ b/core/lib/src/form/validate.rs @@ -159,7 +159,7 @@ crate::export! { /// Equality validator: succeeds exactly when `a` == `b`, using [`PartialEq`]. /// -/// On failure, returns a validation error with the following message: +/// On error, returns a validation error with the following message: /// /// ```text /// value does not match expected value @@ -236,7 +236,7 @@ pub fn dbg_eq<'v, A, B>(a: &A, b: B) -> Result<'v, ()> /// Negative equality validator: succeeds exactly when `a` != `b`, using /// [`PartialEq`]. /// -/// On failure, returns a validation error with the following message: +/// On error, returns a validation error with the following message: /// /// ```text /// value is equal to an invalid value @@ -361,7 +361,7 @@ impl> Len for crate::serde::msgpack::MsgPack { /// Length validator: succeeds when the length of a value is within a `range`. /// -/// The value must implement [`Len`]. On failure, returns an [`InvalidLength`] +/// The value must implement [`Len`]. On error, returns an [`InvalidLength`] /// error. See [`Len`] for supported types and how their length is computed. /// /// [`InvalidLength`]: crate::form::error::ErrorKind::InvalidLength @@ -504,7 +504,7 @@ impl + ?Sized> Contains for &T { /// [`Contains`](Contains) where `I` is the type of the `item`. See /// [`Contains`] for supported types and items. /// -/// On failure, returns a validation error with the following message: +/// On error, returns a validation error with the following message: /// /// ```text /// value is equal to an invalid value @@ -590,7 +590,7 @@ pub fn dbg_contains<'v, V, I>(value: V, item: I) -> Result<'v, ()> /// [`Contains`](Contains) where `I` is the type of the `item`. See /// [`Contains`] for supported types and items. /// -/// On failure, returns a validation error with the following message: +/// On error, returns a validation error with the following message: /// /// ```text /// value contains a disallowed item @@ -670,7 +670,7 @@ pub fn dbg_omits<'v, V, I>(value: V, item: I) -> Result<'v, ()> /// Integer range validator: succeeds when an integer value is within a range. /// /// The value must be an integer type that implement `TryInto + Copy`. On -/// failure, returns an [`OutOfRange`] error. +/// error, returns an [`OutOfRange`] error. /// /// [`OutOfRange`]: crate::form::error::ErrorKind::OutOfRange /// @@ -719,7 +719,7 @@ pub fn range<'v, V, R>(value: &V, range: R) -> Result<'v, ()> /// the `item`. The iterator must be [`Clone`]. See [`Contains`] for supported /// types and items. The item must be [`Debug`]. /// -/// On failure, returns a [`InvalidChoice`] error with the debug representation +/// On error, returns a [`InvalidChoice`] error with the debug representation /// of each item in `items`. /// /// [`InvalidChoice`]: crate::form::error::ErrorKind::InvalidChoice @@ -762,7 +762,7 @@ pub fn one_of<'v, V, I, R>(value: V, items: R) -> Result<'v, ()> /// File type validator: succeeds when a [`TempFile`] has the Content-Type /// `content_type`. /// -/// On failure, returns a validation error with one of the following messages: +/// On error, returns a validation error with one of the following messages: /// /// ```text /// // the file has an incorrect extension @@ -810,7 +810,7 @@ pub fn ext<'v>(file: &TempFile<'_>, r#type: ContentType) -> Result<'v, ()> { /// when a more case-specific option does not exist. It succeeds exactly when /// `f` returns `true` and fails otherwise. /// -/// On failure, returns a validation error with the message `msg`. +/// On error, returns a validation error with the message `msg`. /// /// # Example /// @@ -860,7 +860,7 @@ pub fn with<'v, V, F, M>(value: V, f: F, msg: M) -> Result<'v, ()> /// Along with [`with`], this is the most generic validator. It succeeds /// exactly when `f` returns `Ok` and fails otherwise. /// -/// On failure, returns a validation error with the message in the `Err` +/// On error, returns a validation error with the message in the `Err` /// variant converted into a string. /// /// # Example diff --git a/core/lib/src/fs/server.rs b/core/lib/src/fs/server.rs index da78ec3374..8aac5c259b 100644 --- a/core/lib/src/fs/server.rs +++ b/core/lib/src/fs/server.rs @@ -148,12 +148,12 @@ impl FileServer { if !options.contains(Options::IndexFile) && !path.is_dir() { let path = path.display(); error!("FileServer path '{}' is not a directory.", path.primary()); - warn_!("Aborting early to prevent inevitable handler failure."); + warn_!("Aborting early to prevent inevitable handler error."); panic!("invalid directory: refusing to continue"); } else if !path.exists() { let path = path.display(); error!("FileServer path '{}' is not a file.", path.primary()); - warn_!("Aborting early to prevent inevitable handler failure."); + warn_!("Aborting early to prevent inevitable handler error."); panic!("invalid file: refusing to continue"); } } diff --git a/core/lib/src/outcome.rs b/core/lib/src/outcome.rs index 44a38f89de..549e850910 100644 --- a/core/lib/src/outcome.rs +++ b/core/lib/src/outcome.rs @@ -1,10 +1,10 @@ -//! Success, failure, and forward handling. +//! Success, error, and forward handling. //! //! The `Outcome` type is similar to the standard library's `Result` type. It is an enum with three variants, each containing a value: -//! `Success(S)`, which represents a successful outcome, `Failure(E)`, which -//! represents a failing outcome, and `Forward(F)`, which represents neither a -//! success or failure, but instead, indicates that processing could not be +//! `Success(S)`, which represents a successful outcome, `Error(E)`, which +//! represents an erroring outcome, and `Forward(F)`, which represents neither a +//! success or error, but instead, indicates that processing could not be //! handled and should instead be _forwarded_ to whatever can handle the //! processing next. //! @@ -35,15 +35,15 @@ //! `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`. //! -//! # Failure +//! # Error //! -//! A failure `Outcome`, `Failure(E)`, is returned when a function +//! An error `Outcome`, `Error(E)`, is returned when a function //! fails with some error and no processing can or should continue as a result. -//! The meaning of a failure depends on the context. +//! The meaning of an error depends on the context. //! -//! In Rocket, a `Failure` generally means that a request is taken out of normal +//! In Rocket, an `Error` generally means that a request is taken out of normal //! processing. The request is then given to the catcher corresponding to some -//! status code. Users can catch failures by requesting a type of `Result` +//! status code. Users can catch errors by requesting a type of `Result` //! or `Option` in request handlers. For example, if a user's handler looks //! like: //! @@ -56,10 +56,9 @@ //! ``` //! //! The [`FromData`] 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` -//! value. +//! `Success(S)` and `Error(E)`. If `from_data` returns an `Error`, the `Error` +//! 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` value. //! //! # Forward //! @@ -79,11 +78,11 @@ //! ``` //! //! The [`FromData`] implementation for the type `S` returns an `Outcome` with a -//! `Success(S)`, `Failure(E)`, and `Forward(F)`. If the `Outcome` is a +//! `Success(S)`, `Error(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, //! if there are no non-forwarding routes, forwarded requests are handled by the -//! 404 catcher. Similar to `Failure`s, users can catch `Forward`s by requesting +//! 404 catcher. Similar to `Error`s, users can catch `Forward`s by requesting //! a type of `Option`. If an `Outcome` is a `Forward`, the `Option` will be //! `None`. @@ -93,8 +92,8 @@ use yansi::{Paint, Color}; use self::Outcome::*; -/// An enum representing success (`Success`), failure (`Failure`), or -/// forwarding (`Forward`). +/// An enum representing success (`Success`), error (`Error`), or forwarding +/// (`Forward`). /// /// See the [top level documentation](crate::outcome) for detailed information. #[must_use] @@ -102,24 +101,24 @@ use self::Outcome::*; pub enum Outcome { /// Contains the success value. Success(S), - /// Contains the failure error value. - Failure(E), + /// Contains the error error value. + Error(E), /// Contains the value to forward on. Forward(F), } /// Conversion trait from some type into an Outcome type. pub trait IntoOutcome { - /// The type to use when returning an `Outcome::Failure`. - type Failure: Sized; + /// The type to use when returning an `Outcome::Error`. + type Error: Sized; /// The type to use when returning an `Outcome::Forward`. type Forward: Sized; /// Converts `self` into an `Outcome`. If `self` represents a success, an - /// `Outcome::Success` is returned. Otherwise, an `Outcome::Failure` is - /// returned with `failure` as the inner value. - fn into_outcome(self, failure: Self::Failure) -> Outcome; + /// `Outcome::Success` is returned. Otherwise, an `Outcome::Error` is + /// returned with `error` as the inner value. + fn into_outcome(self, error: Self::Error) -> Outcome; /// Converts `self` into an `Outcome`. If `self` represents a success, an /// `Outcome::Success` is returned. Otherwise, an `Outcome::Forward` is @@ -128,14 +127,14 @@ pub trait IntoOutcome { } impl IntoOutcome for Option { - type Failure = E; + type Error = E; type Forward = F; #[inline] - fn into_outcome(self, failure: E) -> Outcome { + fn into_outcome(self, error: E) -> Outcome { match self { Some(val) => Success(val), - None => Failure(failure) + None => Error(error) } } @@ -208,7 +207,7 @@ impl Outcome { /// let x: Outcome = Success(10); /// assert_eq!(x.is_success(), true); /// - /// let x: Outcome = Failure("Hi! I'm an error."); + /// let x: Outcome = Error("Hi! I'm an error."); /// assert_eq!(x.is_success(), false); /// /// let x: Outcome = Forward(25); @@ -219,7 +218,7 @@ impl Outcome { matches!(self, Success(_)) } - /// Return true if this `Outcome` is a `Failure`. + /// Return true if this `Outcome` is an `Error`. /// /// # Examples /// @@ -228,17 +227,17 @@ impl Outcome { /// # use rocket::outcome::Outcome::*; /// # /// let x: Outcome = Success(10); - /// assert_eq!(x.is_failure(), false); + /// assert_eq!(x.is_error(), false); /// - /// let x: Outcome = Failure("Hi! I'm an error."); - /// assert_eq!(x.is_failure(), true); + /// let x: Outcome = Error("Hi! I'm an error."); + /// assert_eq!(x.is_error(), true); /// /// let x: Outcome = Forward(25); - /// assert_eq!(x.is_failure(), false); + /// assert_eq!(x.is_error(), false); /// ``` #[inline] - pub fn is_failure(&self) -> bool { - matches!(self, Failure(_)) + pub fn is_error(&self) -> bool { + matches!(self, Error(_)) } /// Return true if this `Outcome` is a `Forward`. @@ -252,7 +251,7 @@ impl Outcome { /// let x: Outcome = Success(10); /// assert_eq!(x.is_forward(), false); /// - /// let x: Outcome = Failure("Hi! I'm an error."); + /// let x: Outcome = Error("Hi! I'm an error."); /// assert_eq!(x.is_forward(), false); /// /// let x: Outcome = Forward(25); @@ -275,7 +274,7 @@ impl Outcome { /// let x: Outcome = Success(10); /// assert_eq!(x.succeeded(), Some(10)); /// - /// let x: Outcome = Failure("Hi! I'm an error."); + /// let x: Outcome = Error("Hi! I'm an error."); /// assert_eq!(x.succeeded(), None); /// /// let x: Outcome = Forward(25); @@ -291,7 +290,7 @@ impl Outcome { /// Converts from `Outcome` to `Option`. /// - /// Returns the `Some` of the `Failure` if this is a `Failure`, otherwise + /// Returns the `Some` of the `Error` if this is an `Error`, otherwise /// returns `None`. `self` is consumed, and all other values are discarded. /// /// ```rust @@ -301,7 +300,7 @@ impl Outcome { /// let x: Outcome = Success(10); /// assert_eq!(x.failed(), None); /// - /// let x: Outcome = Failure("Hi! I'm an error."); + /// let x: Outcome = Error("Hi! I'm an error."); /// assert_eq!(x.failed(), Some("Hi! I'm an error.")); /// /// let x: Outcome = Forward(25); @@ -310,7 +309,7 @@ impl Outcome { #[inline] pub fn failed(self) -> Option { match self { - Failure(val) => Some(val), + Error(val) => Some(val), _ => None } } @@ -327,7 +326,7 @@ impl Outcome { /// let x: Outcome = Success(10); /// assert_eq!(x.forwarded(), None); /// - /// let x: Outcome = Failure("Hi! I'm an error."); + /// let x: Outcome = Error("Hi! I'm an error."); /// assert_eq!(x.forwarded(), None); /// /// let x: Outcome = Forward(25); @@ -355,7 +354,7 @@ impl Outcome { /// let x: Outcome = Success(10); /// assert_eq!(x.success_or(false), Ok(10)); /// - /// let x: Outcome = Failure("Hi! I'm an error."); + /// let x: Outcome = Error("Hi! I'm an error."); /// assert_eq!(x.success_or(false), Err(false)); /// /// let x: Outcome = Forward(25); @@ -384,7 +383,7 @@ impl Outcome { /// let x: Outcome = Success(10); /// assert_eq!(x.success_or_else(|| false), Ok(10)); /// - /// let x: Outcome = Failure("Hi! I'm an error."); + /// let x: Outcome = Error("Hi! I'm an error."); /// assert_eq!(x.success_or_else(|| false), Err(false)); /// /// let x: Outcome = Forward(25); @@ -407,14 +406,14 @@ impl Outcome { /// let x: Outcome = Success(10); /// assert_eq!(x.as_ref(), Success(&10)); /// - /// let x: Outcome = Failure("Hi! I'm an error."); - /// assert_eq!(x.as_ref(), Failure(&"Hi! I'm an error.")); + /// let x: Outcome = Error("Hi! I'm an error."); + /// assert_eq!(x.as_ref(), Error(&"Hi! I'm an error.")); /// ``` #[inline] pub fn as_ref(&self) -> Outcome<&S, &E, &F> { match *self { Success(ref val) => Success(val), - Failure(ref val) => Failure(val), + Error(ref val) => Error(val), Forward(ref val) => Forward(val), } } @@ -436,7 +435,7 @@ impl Outcome { pub fn as_mut(&mut self) -> Outcome<&mut S, &mut E, &mut F> { match *self { Success(ref mut val) => Success(val), - Failure(ref mut val) => Failure(val), + Error(ref mut val) => Error(val), Forward(ref mut val) => Forward(val), } } @@ -458,29 +457,29 @@ impl Outcome { pub fn map T>(self, f: M) -> Outcome { match self { Success(val) => Success(f(val)), - Failure(val) => Failure(val), + Error(val) => Error(val), Forward(val) => Forward(val), } } - /// Maps the `Failure` value using `f`. Maps an `Outcome` to an + /// Maps the `Error` value using `f`. Maps an `Outcome` to an /// `Outcome` by applying the function `f` to the value of type `E` - /// in `self` if `self` is an `Outcome::Failure`. + /// in `self` if `self` is an `Outcome::Error`. /// /// ```rust /// # use rocket::outcome::Outcome; /// # use rocket::outcome::Outcome::*; /// # - /// let x: Outcome = Failure("hi"); + /// let x: Outcome = Error("hi"); /// - /// let mapped = x.map_failure(|v| if v == "hi" { 10 } else { 0 }); - /// assert_eq!(mapped, Failure(10)); + /// let mapped = x.map_error(|v| if v == "hi" { 10 } else { 0 }); + /// assert_eq!(mapped, Error(10)); /// ``` #[inline] - pub fn map_failure T>(self, f: M) -> Outcome { + pub fn map_error T>(self, f: M) -> Outcome { match self { Success(val) => Success(val), - Failure(val) => Failure(f(val)), + Error(val) => Error(f(val)), Forward(val) => Forward(val), } } @@ -502,7 +501,7 @@ impl Outcome { pub fn map_forward T>(self, f: M) -> Outcome { match self { Success(val) => Success(val), - Failure(val) => Failure(val), + Error(val) => Error(val), Forward(val) => Forward(f(val)), } } @@ -523,7 +522,7 @@ impl Outcome { /// let mapped = x.and_then(|v| match v { /// 10 => Success("10"), /// 1 => Forward(false), - /// _ => Failure("30") + /// _ => Error("30") /// }); /// /// assert_eq!(mapped, Success("10")); @@ -532,15 +531,15 @@ impl Outcome { pub fn and_then Outcome>(self, f: M) -> Outcome { match self { Success(val) => f(val), - Failure(val) => Failure(val), + Error(val) => Error(val), Forward(val) => Forward(val), } } /// Converts from `Outcome` to `Outcome` using `f` to map - /// `Failure(E)` to `Failure(T)`. + /// `Error(E)` to `Error(T)`. /// - /// If `self` is not `Failure`, `self` is returned. + /// If `self` is not `Error`, `self` is returned. /// /// # Examples /// @@ -548,21 +547,21 @@ impl Outcome { /// # use rocket::outcome::Outcome; /// # use rocket::outcome::Outcome::*; /// # - /// let x: Outcome = Failure("hi"); + /// let x: Outcome = Error("hi"); /// - /// let mapped = x.failure_then(|v| match v { - /// "hi" => Failure(10), + /// let mapped = x.error_then(|v| match v { + /// "hi" => Error(10), /// "test" => Forward(false), /// _ => Success(10) /// }); /// - /// assert_eq!(mapped, Failure(10)); + /// assert_eq!(mapped, Error(10)); /// ``` #[inline] - pub fn failure_then Outcome>(self, f: M) -> Outcome { + pub fn error_then Outcome>(self, f: M) -> Outcome { match self { Success(val) => Success(val), - Failure(val) => f(val), + Error(val) => f(val), Forward(val) => Forward(val), } } @@ -583,7 +582,7 @@ impl Outcome { /// let mapped = x.forward_then(|v| match v { /// Some(true) => Success(10), /// Some(false) => Forward(20), - /// None => Failure("10") + /// None => Error("10") /// }); /// /// assert_eq!(mapped, Forward(20)); @@ -592,13 +591,13 @@ impl Outcome { pub fn forward_then Outcome>(self, f: M) -> Outcome { match self { Success(val) => Success(val), - Failure(val) => Failure(val), + Error(val) => Error(val), Forward(val) => f(val), } } /// Converts `Outcome` to `Result` by identity mapping - /// `Success(S)` and `Failure(E)` to `Result` and mapping `Forward(F)` + /// `Success(S)` and `Error(E)` to `Result` and mapping `Forward(F)` /// to `Result` using `f`. /// /// ```rust @@ -608,7 +607,7 @@ impl Outcome { /// let x: Outcome = Success(10); /// assert_eq!(x.ok_map_forward(|x| Ok(x as i32 + 1)), Ok(10)); /// - /// let x: Outcome = Failure("hello"); + /// let x: Outcome = Error("hello"); /// assert_eq!(x.ok_map_forward(|x| Ok(x as i32 + 1)), Err("hello")); /// /// let x: Outcome = Forward(0); @@ -620,13 +619,13 @@ impl Outcome { { match self { Outcome::Success(s) => Ok(s), - Outcome::Failure(e) => Err(e), + Outcome::Error(e) => Err(e), Outcome::Forward(v) => f(v), } } /// Converts `Outcome` to `Result` by identity mapping - /// `Success(S)` and `Forward(F)` to `Result` and mapping `Failure(E)` + /// `Success(S)` and `Forward(F)` to `Result` and mapping `Error(E)` /// to `Result` using `f`. /// /// ```rust @@ -634,21 +633,21 @@ impl Outcome { /// # use rocket::outcome::Outcome::*; /// # /// let x: Outcome = Success(10); - /// assert_eq!(x.ok_map_failure(|s| Ok(123)), Ok(10)); + /// assert_eq!(x.ok_map_error(|s| Ok(123)), Ok(10)); /// - /// let x: Outcome = Failure("hello"); - /// assert_eq!(x.ok_map_failure(|s| Ok(123)), Ok(123)); + /// let x: Outcome = Error("hello"); + /// assert_eq!(x.ok_map_error(|s| Ok(123)), Ok(123)); /// /// let x: Outcome = Forward(0); - /// assert_eq!(x.ok_map_failure(|s| Ok(123)), Err(0)); + /// assert_eq!(x.ok_map_error(|s| Ok(123)), Err(0)); /// ``` #[inline] - pub fn ok_map_failure(self, f: M) -> Result + pub fn ok_map_error(self, f: M) -> Result where M: FnOnce(E) -> Result { match self { Outcome::Success(s) => Ok(s), - Outcome::Failure(e) => f(e), + Outcome::Error(e) => f(e), Outcome::Forward(v) => Err(v), } } @@ -657,7 +656,7 @@ impl Outcome { fn formatting(&self) -> (Color, &'static str) { match *self { Success(..) => (Color::Green, "Success"), - Failure(..) => (Color::Red, "Failure"), + Error(..) => (Color::Red, "Error"), Forward(..) => (Color::Yellow, "Forward"), } } @@ -675,7 +674,7 @@ impl<'a, S: Send + 'a, E: Send + 'a, F: Send + 'a> Outcome { crate::export! { /// Unwraps a [`Success`](Outcome::Success) or propagates a `Forward` or - /// `Failure`. + /// `Error` by returning early. /// /// # Syntax /// @@ -691,7 +690,7 @@ crate::export! { /// ``` /// /// This is just like `?` (or previously, `try!`), but for `Outcome`. In the - /// case of a `Forward` or `Failure` variant, the inner type is passed to + /// case of a `Forward` or `Error` variant, the inner type is passed to /// [`From`](std::convert::From), allowing for the conversion between /// specific and more general types. The resulting forward/error is /// immediately returned. Because of the early return, `try_outcome!` can @@ -746,8 +745,8 @@ crate::export! { macro_rules! try_outcome { ($expr:expr $(,)?) => (match $expr { $crate::outcome::Outcome::Success(val) => val, - $crate::outcome::Outcome::Failure(e) => { - return $crate::outcome::Outcome::Failure(::std::convert::From::from(e)) + $crate::outcome::Outcome::Error(e) => { + return $crate::outcome::Outcome::Error(::std::convert::From::from(e)) }, $crate::outcome::Outcome::Forward(f) => { return $crate::outcome::Outcome::Forward(::std::convert::From::from(f)) diff --git a/core/lib/src/request/from_request.rs b/core/lib/src/request/from_request.rs index 33aa5ef86e..3d048e8541 100644 --- a/core/lib/src/request/from_request.rs +++ b/core/lib/src/request/from_request.rs @@ -1,3 +1,4 @@ +use std::convert::Infallible; use std::fmt::Debug; use std::net::{IpAddr, SocketAddr}; @@ -12,14 +13,14 @@ use crate::http::uri::{Host, Origin}; pub type Outcome = outcome::Outcome; impl IntoOutcome for Result { - type Failure = Status; + type Error = Status; type Forward = Status; #[inline] fn into_outcome(self, status: Status) -> Outcome { match self { Ok(val) => Success(val), - Err(err) => Failure((status, err)) + Err(err) => Error((status, err)) } } @@ -85,8 +86,8 @@ impl IntoOutcome for Result { /// ``` /// /// Request guards always fire in left-to-right declaration order. In the -/// example above, the order is `a` followed by `b` followed by `c`. Failure is -/// short-circuiting; if one guard fails, the remaining are not attempted. +/// example above, the order is `a` followed by `b` followed by `c`. Errors are +/// short-circuiting; if one guard errors, the remaining are not attempted. /// /// # Outcomes /// @@ -99,12 +100,12 @@ impl IntoOutcome for Result { /// the value for the corresponding parameter. As long as all other guards /// succeed, the request will be handled. /// -/// * **Failure**(Status, E) +/// * **Error**(Status, E) /// -/// If the `Outcome` is [`Failure`], the request will fail with the given +/// 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` and `Option` to catch `Failure`s and retrieve the +/// of `Result` and `Option` to catch `Error`s and retrieve the /// error value. /// /// * **Forward**(Status) @@ -191,7 +192,7 @@ impl IntoOutcome for Result { /// /// The type `T` is derived from the incoming request using `T`'s /// `FromRequest` implementation. If derivation is a `Success`, the value is -/// returned in `Ok`. If the derivation is a `Failure`, the error value is +/// returned in `Ok`. If the derivation is an `Error`, the error value is /// returned in `Err`. If the derivation is a `Forward`, the request is /// forwarded with the same status code as the original forward. /// @@ -232,9 +233,9 @@ impl IntoOutcome for Result { /// } /// /// match req.headers().get_one("x-api-key") { -/// None => Outcome::Failure((Status::BadRequest, ApiKeyError::Missing)), +/// None => Outcome::Error((Status::BadRequest, ApiKeyError::Missing)), /// Some(key) if is_valid(key) => Outcome::Success(ApiKey(key)), -/// Some(_) => Outcome::Failure((Status::BadRequest, ApiKeyError::Invalid)), +/// Some(_) => Outcome::Error((Status::BadRequest, ApiKeyError::Invalid)), /// } /// } /// } @@ -390,7 +391,7 @@ pub trait FromRequest<'r>: Sized { /// Derives an instance of `Self` from the incoming request metadata. /// /// If the derivation is successful, an outcome of `Success` is returned. If - /// the derivation fails in an unrecoverable fashion, `Failure` is returned. + /// the derivation fails in an unrecoverable fashion, `Error` is returned. /// `Forward` is returned to indicate that the request should be forwarded /// to other matching routes, if any. async fn from_request(request: &'r Request<'_>) -> Outcome; @@ -398,27 +399,27 @@ pub trait FromRequest<'r>: Sized { #[crate::async_trait] impl<'r> FromRequest<'r> for Method { - type Error = std::convert::Infallible; + type Error = Infallible; - async fn from_request(request: &'r Request<'_>) -> Outcome { + async fn from_request(request: &'r Request<'_>) -> Outcome { Success(request.method()) } } #[crate::async_trait] impl<'r> FromRequest<'r> for &'r Origin<'r> { - type Error = std::convert::Infallible; + type Error = Infallible; - async fn from_request(request: &'r Request<'_>) -> Outcome { + async fn from_request(request: &'r Request<'_>) -> Outcome { Success(request.uri()) } } #[crate::async_trait] impl<'r> FromRequest<'r> for &'r Host<'r> { - type Error = std::convert::Infallible; + type Error = Infallible; - async fn from_request(request: &'r Request<'_>) -> Outcome { + async fn from_request(request: &'r Request<'_>) -> Outcome { match request.host() { Some(host) => Success(host), None => Forward(Status::NotFound) @@ -428,9 +429,9 @@ impl<'r> FromRequest<'r> for &'r Host<'r> { #[crate::async_trait] impl<'r> FromRequest<'r> for &'r Route { - type Error = std::convert::Infallible; + type Error = Infallible; - async fn from_request(request: &'r Request<'_>) -> Outcome { + async fn from_request(request: &'r Request<'_>) -> Outcome { match request.route() { Some(route) => Success(route), None => Forward(Status::InternalServerError) @@ -440,18 +441,18 @@ impl<'r> FromRequest<'r> for &'r Route { #[crate::async_trait] impl<'r> FromRequest<'r> for &'r CookieJar<'r> { - type Error = std::convert::Infallible; + type Error = Infallible; - async fn from_request(request: &'r Request<'_>) -> Outcome { + async fn from_request(request: &'r Request<'_>) -> Outcome { Success(request.cookies()) } } #[crate::async_trait] impl<'r> FromRequest<'r> for &'r Accept { - type Error = std::convert::Infallible; + type Error = Infallible; - async fn from_request(request: &'r Request<'_>) -> Outcome { + async fn from_request(request: &'r Request<'_>) -> Outcome { match request.accept() { Some(accept) => Success(accept), None => Forward(Status::NotFound) @@ -461,9 +462,9 @@ impl<'r> FromRequest<'r> for &'r Accept { #[crate::async_trait] impl<'r> FromRequest<'r> for &'r ContentType { - type Error = std::convert::Infallible; + type Error = Infallible; - async fn from_request(request: &'r Request<'_>) -> Outcome { + async fn from_request(request: &'r Request<'_>) -> Outcome { match request.content_type() { Some(content_type) => Success(content_type), None => Forward(Status::NotFound) @@ -473,9 +474,9 @@ impl<'r> FromRequest<'r> for &'r ContentType { #[crate::async_trait] impl<'r> FromRequest<'r> for IpAddr { - type Error = std::convert::Infallible; + type Error = Infallible; - async fn from_request(request: &'r Request<'_>) -> Outcome { + async fn from_request(request: &'r Request<'_>) -> Outcome { match request.client_ip() { Some(addr) => Success(addr), None => Forward(Status::NotFound) @@ -485,9 +486,9 @@ impl<'r> FromRequest<'r> for IpAddr { #[crate::async_trait] impl<'r> FromRequest<'r> for SocketAddr { - type Error = std::convert::Infallible; + type Error = Infallible; - async fn from_request(request: &'r Request<'_>) -> Outcome { + async fn from_request(request: &'r Request<'_>) -> Outcome { match request.remote() { Some(addr) => Success(addr), None => Forward(Status::NotFound) @@ -497,12 +498,12 @@ impl<'r> FromRequest<'r> for SocketAddr { #[crate::async_trait] impl<'r, T: FromRequest<'r>> FromRequest<'r> for Result { - type Error = std::convert::Infallible; + type Error = Infallible; - async fn from_request(request: &'r Request<'_>) -> Outcome { + async fn from_request(request: &'r Request<'_>) -> Outcome { match T::from_request(request).await { Success(val) => Success(Ok(val)), - Failure((_, e)) => Success(Err(e)), + Error((_, e)) => Success(Err(e)), Forward(status) => Forward(status), } } @@ -510,21 +511,21 @@ impl<'r, T: FromRequest<'r>> FromRequest<'r> for Result { #[crate::async_trait] impl<'r, T: FromRequest<'r>> FromRequest<'r> for Option { - type Error = std::convert::Infallible; + type Error = Infallible; - async fn from_request(request: &'r Request<'_>) -> Outcome { + async fn from_request(request: &'r Request<'_>) -> Outcome { match T::from_request(request).await { Success(val) => Success(Some(val)), - Failure(_) | Forward(_) => Success(None), + Error(_) | Forward(_) => Success(None), } } } #[crate::async_trait] impl<'r, T: FromRequest<'r>> FromRequest<'r> for Outcome { - type Error = std::convert::Infallible; + type Error = Infallible; - async fn from_request(request: &'r Request<'_>) -> Outcome { + async fn from_request(request: &'r Request<'_>) -> Outcome { Success(T::from_request(request).await) } } diff --git a/core/lib/src/response/responder.rs b/core/lib/src/response/responder.rs index c6fbdd8b26..3c1ba95699 100644 --- a/core/lib/src/response/responder.rs +++ b/core/lib/src/response/responder.rs @@ -111,7 +111,7 @@ use crate::request::Request; /// * An `Ok(Response)` indicates success. The `Response` will be written out /// to the client. /// -/// * An `Err(Status)` indicates failure. The error catcher for `Status` will +/// * An `Err(Status)` indicates an error. The error catcher for `Status` will /// be invoked to generate a response. /// /// # Implementation Tips diff --git a/core/lib/src/route/handler.rs b/core/lib/src/route/handler.rs index 9fee0d67a8..d139942b62 100644 --- a/core/lib/src/route/handler.rs +++ b/core/lib/src/route/handler.rs @@ -141,7 +141,7 @@ pub trait Handler: Cloneable + Send + Sync + 'static { /// The variant of `Outcome` returned by the returned `Future` determines /// what Rocket does next. If the return value is a `Success(Response)`, the /// wrapped `Response` is used to respond to the client. If the return value - /// is a `Failure(Status)`, the error catcher for `Status` is invoked to + /// is an `Error(Status)`, the error catcher for `Status` is invoked to /// generate a response. Otherwise, if the return value is `Forward(Data)`, /// the next matching route is attempted. If there are no other matching /// routes, the `404` error catcher is invoked. @@ -172,7 +172,7 @@ impl<'r, 'o: 'r> Outcome<'o> { /// Return the `Outcome` of response to `req` from `responder`. /// /// If the responder returns `Ok`, an outcome of `Success` is returned with - /// the response. If the responder returns `Err`, an outcome of `Failure` is + /// the response. If the responder returns `Err`, an outcome of `Error` is /// returned with the status code. /// /// # Example @@ -188,14 +188,14 @@ impl<'r, 'o: 'r> Outcome<'o> { pub fn from>(req: &'r Request<'_>, responder: R) -> Outcome<'r> { match responder.respond_to(req) { Ok(response) => Outcome::Success(response), - Err(status) => Outcome::Failure(status) + Err(status) => Outcome::Error(status) } } /// Return the `Outcome` of response to `req` from `responder`. /// /// If the responder returns `Ok`, an outcome of `Success` is returned with - /// the response. If the responder returns `Err`, an outcome of `Failure` is + /// the response. If the responder returns `Err`, an outcome of `Error` is /// returned with the status code. /// /// # Example @@ -214,7 +214,7 @@ impl<'r, 'o: 'r> Outcome<'o> { let responder = result.map_err(crate::response::Debug); match responder.respond_to(req) { Ok(response) => Outcome::Success(response), - Err(status) => Outcome::Failure(status) + Err(status) => Outcome::Error(status) } } @@ -243,8 +243,8 @@ impl<'r, 'o: 'r> Outcome<'o> { } } - /// Return an `Outcome` of `Failure` with the status code `code`. This is - /// equivalent to `Outcome::Failure(code)`. + /// Return an `Outcome` of `Error` with the status code `code`. This is + /// equivalent to `Outcome::Error(code)`. /// /// This method exists to be used during manual routing. /// @@ -255,12 +255,12 @@ impl<'r, 'o: 'r> Outcome<'o> { /// use rocket::http::Status; /// /// fn bad_req_route<'r>(_: &'r Request, _: Data<'r>) -> route::Outcome<'r> { - /// route::Outcome::failure(Status::BadRequest) + /// route::Outcome::error(Status::BadRequest) /// } /// ``` #[inline(always)] - pub fn failure(code: Status) -> Outcome<'r> { - Outcome::Failure(code) + pub fn error(code: Status) -> Outcome<'r> { + Outcome::Error(code) } /// Return an `Outcome` of `Forward` with the data `data`. This is diff --git a/core/lib/src/route/route.rs b/core/lib/src/route/route.rs index 24853d9517..e78275b91b 100644 --- a/core/lib/src/route/route.rs +++ b/core/lib/src/route/route.rs @@ -64,7 +64,7 @@ use crate::sentinel::Sentry; /// higher precedence during routing than routes with higher ranks. Thus, routes /// are attempted in ascending rank order. If a higher precedence route returns /// an `Outcome` of `Forward`, the next highest precedence route is attempted, -/// and so on, until a route returns `Success` or `Failure`, or there are no +/// and so on, until a route returns `Success` or `Error`, or there are no /// more routes to try. When all routes have been attempted, Rocket issues a /// `404` error, handled by the appropriate [`Catcher`](crate::Catcher). /// diff --git a/core/lib/src/serde/json.rs b/core/lib/src/serde/json.rs index dfc85a6ba1..a96f629e85 100644 --- a/core/lib/src/serde/json.rs +++ b/core/lib/src/serde/json.rs @@ -201,12 +201,12 @@ impl<'r, T: Deserialize<'r>> FromData<'r> for Json { match Self::from_data(req, data).await { Ok(value) => Outcome::Success(value), Err(Error::Io(e)) if e.kind() == io::ErrorKind::UnexpectedEof => { - Outcome::Failure((Status::PayloadTooLarge, Error::Io(e))) + Outcome::Error((Status::PayloadTooLarge, Error::Io(e))) }, Err(Error::Parse(s, e)) if e.classify() == serde_json::error::Category::Data => { - Outcome::Failure((Status::UnprocessableEntity, Error::Parse(s, e))) + Outcome::Error((Status::UnprocessableEntity, Error::Parse(s, e))) }, - Err(e) => Outcome::Failure((Status::BadRequest, e)), + Err(e) => Outcome::Error((Status::BadRequest, e)), } } diff --git a/core/lib/src/serde/msgpack.rs b/core/lib/src/serde/msgpack.rs index e77c6aa76f..3b92f06092 100644 --- a/core/lib/src/serde/msgpack.rs +++ b/core/lib/src/serde/msgpack.rs @@ -170,15 +170,15 @@ impl<'r, T: Deserialize<'r>> FromData<'r> for MsgPack { match Self::from_data(req, data).await { Ok(value) => Outcome::Success(value), Err(Error::InvalidDataRead(e)) if e.kind() == io::ErrorKind::UnexpectedEof => { - Outcome::Failure((Status::PayloadTooLarge, Error::InvalidDataRead(e))) + Outcome::Error((Status::PayloadTooLarge, Error::InvalidDataRead(e))) }, | Err(e@Error::TypeMismatch(_)) | Err(e@Error::OutOfRange) | Err(e@Error::LengthMismatch(_)) => { - Outcome::Failure((Status::UnprocessableEntity, e)) + Outcome::Error((Status::UnprocessableEntity, e)) }, - Err(e) => Outcome::Failure((Status::BadRequest, e)), + Err(e) => Outcome::Error((Status::BadRequest, e)), } } } diff --git a/core/lib/src/serde/uuid.rs b/core/lib/src/serde/uuid.rs index 8e66465710..8c377168c6 100644 --- a/core/lib/src/serde/uuid.rs +++ b/core/lib/src/serde/uuid.rs @@ -79,7 +79,7 @@ use crate::request::FromParam; use crate::form::{self, FromFormField, ValueField}; -/// Error returned on [`FromParam`] or [`FromFormField`] failure. +/// Error returned on [`FromParam`] or [`FromFormField`] failures. /// pub use uuid_::Error; diff --git a/core/lib/src/server.rs b/core/lib/src/server.rs index e3836984fd..e874b0ff6b 100644 --- a/core/lib/src/server.rs +++ b/core/lib/src/server.rs @@ -114,7 +114,7 @@ async fn hyper_service_fn( } impl Rocket { - /// Wrapper around `_send_response` to log a success or failure. + /// Wrapper around `_send_response` to log a success or error. #[inline] async fn send_response( &self, @@ -290,12 +290,12 @@ impl Rocket { request._set_method(Method::Get); match self.route(request, data).await { Outcome::Success(response) => response, - Outcome::Failure(status) => self.handle_error(status, request).await, + Outcome::Error(status) => self.handle_error(status, request).await, Outcome::Forward((_, status)) => self.handle_error(status, request).await, } } Outcome::Forward((_, status)) => self.handle_error(status, request).await, - Outcome::Failure(status) => self.handle_error(status, request).await, + Outcome::Error(status) => self.handle_error(status, request).await, }; // Set the cookies. Note that error responses will only include cookies @@ -310,7 +310,7 @@ impl Rocket { /// Tries to find a `Responder` for a given `request`. It does this by /// routing the request and calling the handler for each matching route - /// until one of the handlers returns success or failure, or there are no + /// until one of the handlers returns success or error, or there are no /// additional routes to try (forward). The corresponding outcome for each /// condition is returned. #[inline] @@ -329,14 +329,14 @@ impl Rocket { let name = route.name.as_deref(); let outcome = handle(name, || route.handler.handle(request, data)).await - .unwrap_or(Outcome::Failure(Status::InternalServerError)); + .unwrap_or(Outcome::Error(Status::InternalServerError)); // Check if the request processing completed (Some) or if the // request needs to be forwarded. If it does, continue the loop // (None) to try again. info_!("{} {}", "Outcome:".primary().bold(), outcome); match outcome { - o@Outcome::Success(_) | o@Outcome::Failure(_) => return o, + o@Outcome::Success(_) | o@Outcome::Error(_) => return o, Outcome::Forward(forwarded) => (data, status) = forwarded, } } @@ -380,7 +380,7 @@ impl Rocket { // Invokes the catcher for `status`. Returns the response on success. // - // On catcher failure, the 500 error catcher is attempted. If _that_ fails, + // On catcher error, the 500 error catcher is attempted. If _that_ errors, // the (infallible) default 500 error cather is used. pub(crate) async fn handle_error<'s, 'r: 's>( &'s self, diff --git a/core/lib/src/state.rs b/core/lib/src/state.rs index 6b5edcdf28..9ecf24e173 100644 --- a/core/lib/src/state.rs +++ b/core/lib/src/state.rs @@ -202,7 +202,7 @@ impl<'r, T: Send + Sync + 'static> FromRequest<'r> for &'r State { Some(state) => Outcome::Success(state), None => { error_!("Attempted to retrieve unmanaged state `{}`!", type_name::()); - Outcome::Failure((Status::InternalServerError, ())) + Outcome::Error((Status::InternalServerError, ())) } } } diff --git a/examples/manual-routing/src/main.rs b/examples/manual-routing/src/main.rs index 704b69c705..2da4c42ad2 100644 --- a/examples/manual-routing/src/main.rs +++ b/examples/manual-routing/src/main.rs @@ -38,7 +38,7 @@ fn upload<'r>(req: &'r Request, data: Data<'r>) -> route::BoxFuture<'r> { Box::pin(async move { if !req.content_type().map_or(false, |ct| ct.is_plain()) { println!(" => Content-Type of upload must be text/plain. Ignoring."); - return route::Outcome::failure(Status::BadRequest); + return route::Outcome::error(Status::BadRequest); } let path = req.rocket().config().temp_dir.relative().join("upload.txt"); @@ -49,10 +49,10 @@ fn upload<'r>(req: &'r Request, data: Data<'r>) -> route::BoxFuture<'r> { } println!(" => Failed copying."); - route::Outcome::failure(Status::InternalServerError) + route::Outcome::error(Status::InternalServerError) } else { println!(" => Couldn't open file: {:?}", file.unwrap_err()); - route::Outcome::failure(Status::InternalServerError) + route::Outcome::error(Status::InternalServerError) } }) } diff --git a/site/guide/4-requests.md b/site/guide/4-requests.md index 1a1a8c637e..56b927cebe 100644 --- a/site/guide/4-requests.md +++ b/site/guide/4-requests.md @@ -263,7 +263,7 @@ would never forward. An `Ok` variant would indicate that `` 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 failure. + you can use an `Option` or `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 @@ -353,7 +353,7 @@ fn index(param: isize, a: A, b: B, c: C) { /* ... */ } ``` Request guards always fire in left-to-right declaration order. In the example -above, the order will be `A` followed by `B` followed by `C`. Failure is +above, the order will be `A` followed by `B` followed by `C`. Errors are short-circuiting; if one guard fails, the remaining are not attempted. To learn more about request guards and implementing them, see the [`FromRequest`] documentation. @@ -484,7 +484,7 @@ The three routes above encode authentication _and_ authorization. The the admin panel displayed. If the user is not an admin, the `AdminUser` guard will forward. Since the `admin_panel_user` route is ranked next highest, it is attempted next. This route succeeds if there is _any_ user signed in, and an -authorization failure message is displayed. Finally, if a user isn't signed in, +authorization error message is displayed. Finally, if a user isn't signed in, the `admin_panel_redirect` route is attempted. Since this route has no guards, it always succeeds. The user is redirected to a log in page. @@ -522,7 +522,7 @@ If the `User` guard forwards or fails, the `Option` will be `None`. If it succeeds, it will be `Some(User)`. For guards that may fail (and not just forward), the `Result` guard allows -retrieving the error type `E` on failure. As an example, when the +retrieving the error type `E` on error. As an example, when the [`mtls::Certificate`] type fails, it reports the reason in an [`mtls::Error`] type. The value can be retrieved in a handler by using a `Result` guard: @@ -876,7 +876,7 @@ fields implement [`FromForm`], or equivalently, [`FromFormField`]. 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 failure can +invalid, a customizable error is returned. As before, a forward or error can be caught by using the `Option` and `Result` types: ```rust diff --git a/site/guide/5-responses.md b/site/guide/5-responses.md index 2a3889500c..56656d91ac 100644 --- a/site/guide/5-responses.md +++ b/site/guide/5-responses.md @@ -289,7 +289,7 @@ fn handler() -> &'static str { `Option` is a _wrapping_ responder: an `Option` can only be returned when `T` implements `Responder`. If the `Option` is `Some`, the wrapped responder is used -to respond to the client. Otherwise, a error of **404 - Not Found** is returned +to respond to the client. Otherwise, an error of **404 - Not Found** is returned to the client. This implementation makes `Option` a convenient type to return when it is not diff --git a/site/tests/src/lib.rs b/site/tests/src/lib.rs index 33114eee03..2bf0a0b219 100644 --- a/site/tests/src/lib.rs +++ b/site/tests/src/lib.rs @@ -26,7 +26,7 @@ macro_rules! assert_form_parses { Ok(v) => assert_eq!(v, $value, "{}", $form), Err(e) => { eprintln!("form failed to parse\n> form: {:?}\n> error: {:?}", $form, e); - panic!("form parse failure"); + panic!("form parse error"); } } ); From fbd1a0d06905dd359307ed584c46f07df02e2c94 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Tue, 31 Oct 2023 18:27:03 -0500 Subject: [PATCH 17/22] Improve forwarding status code precision. Previously, the `NotFound` status code was used to signal many kinds of recoverable, forwarding errors. This included validation errors, incorrect Content-Type errors, and more. This commit modifies the status code used to forward in these instances to more precisely indicate the forwarding condition. In particular: * Parameter `FromParam` errors now forward as 422 (`UnprocessableEntity`). * Query paramater errors now forward as 422 (`UnprocessableEntity`). * Use of incorrect form content-type forwards as 413 (`UnsupportedMediaType`). * `WebSocket` guard now forwards as 400 (`BadRequest`). * `&Host`, `&Accept`, `&ContentType`, `IpAddr`, and `SocketAddr` all forward with a 500 (`InternalServerError`). Additionally, the `IntoOutcome` trait was overhauled to support functionality previously offered by methods on `Outcome`. The `Outcome::forward()` method now requires a status code to use for the forwarding outcome. Finally, logging of `Outcome`s now includes the relevant status code. Resolves #2626. --- contrib/sync_db_pools/lib/src/connection.rs | 2 +- contrib/ws/src/websocket.rs | 4 +- core/codegen/src/attribute/route/mod.rs | 4 +- core/codegen/tests/route.rs | 2 +- core/lib/src/data/data.rs | 2 +- core/lib/src/data/from_data.rs | 27 +-- core/lib/src/form/parser.rs | 2 +- core/lib/src/fs/server.rs | 28 +-- core/lib/src/fs/temp_file.rs | 2 +- core/lib/src/mtls.rs | 2 +- core/lib/src/outcome.rs | 202 +++++++++++++++----- core/lib/src/request/from_request.rs | 56 ++---- core/lib/src/response/flash.rs | 2 +- core/lib/src/route/handler.rs | 36 +--- core/lib/src/server.rs | 2 +- examples/error-handling/src/tests.rs | 20 +- examples/hello/src/tests.rs | 4 +- examples/manual-routing/src/main.rs | 4 +- examples/serialization/src/tests.rs | 5 +- 19 files changed, 233 insertions(+), 173 deletions(-) diff --git a/contrib/sync_db_pools/lib/src/connection.rs b/contrib/sync_db_pools/lib/src/connection.rs index c395dc2866..73c6913b48 100644 --- a/contrib/sync_db_pools/lib/src/connection.rs +++ b/contrib/sync_db_pools/lib/src/connection.rs @@ -210,7 +210,7 @@ impl<'r, K: 'static, C: Poolable> FromRequest<'r> for Connection { #[inline] async fn from_request(request: &'r Request<'_>) -> Outcome { match request.rocket().state::>() { - Some(c) => c.get().await.into_outcome((Status::ServiceUnavailable, ())), + Some(c) => c.get().await.or_error((Status::ServiceUnavailable, ())), None => { error_!("Missing database fairing for `{}`", std::any::type_name::()); Outcome::Error((Status::InternalServerError, ())) diff --git a/contrib/ws/src/websocket.rs b/contrib/ws/src/websocket.rs index 6ad295ac85..32b598e818 100644 --- a/contrib/ws/src/websocket.rs +++ b/contrib/ws/src/websocket.rs @@ -30,7 +30,7 @@ use crate::result::{Result, Error}; /// ### Forwarding /// /// If the incoming request is not a valid WebSocket request, the guard -/// forwards. The guard never fails. +/// forwards with a status of `BadRequest`. The guard never fails. pub struct WebSocket { config: Config, key: String, @@ -203,7 +203,7 @@ impl<'r> FromRequest<'r> for WebSocket { let key = headers.get_one("Sec-WebSocket-Key").map(|k| derive_accept_key(k.as_bytes())); match key { Some(key) if is_upgrade && is_ws && is_13 => Outcome::Success(WebSocket::new(key)), - Some(_) | None => Outcome::Forward(Status::NotFound) + Some(_) | None => Outcome::Forward(Status::BadRequest) } } } diff --git a/core/codegen/src/attribute/route/mod.rs b/core/codegen/src/attribute/route/mod.rs index e30117db00..c2e3043ab6 100644 --- a/core/codegen/src/attribute/route/mod.rs +++ b/core/codegen/src/attribute/route/mod.rs @@ -105,7 +105,7 @@ fn query_decls(route: &Route) -> Option { if !__e.is_empty() { #_log::warn_!("Query string failed to match route declaration."); for _err in __e { #_log::warn_!("{}", _err); } - return #Outcome::Forward((#__data, #Status::NotFound)); + return #Outcome::Forward((#__data, #Status::UnprocessableEntity)); } (#(#ident.unwrap()),*) @@ -146,7 +146,7 @@ fn param_guard_decl(guard: &Guard) -> TokenStream { #_log::warn_!("Parameter guard `{}: {}` is forwarding: {:?}.", #name, stringify!(#ty), __error); - #Outcome::Forward((#__data, #Status::NotFound)) + #Outcome::Forward((#__data, #Status::UnprocessableEntity)) }); // All dynamic parameters should be found if this function is being called; diff --git a/core/codegen/tests/route.rs b/core/codegen/tests/route.rs index d8e4509418..de65f2fa65 100644 --- a/core/codegen/tests/route.rs +++ b/core/codegen/tests/route.rs @@ -269,7 +269,7 @@ fn test_query_collection() { let colors = &["red"]; let dog = &["name=Fido"]; - assert_eq!(run(&client, colors, dog).0, Status::NotFound); + assert_eq!(run(&client, colors, dog).0, Status::UnprocessableEntity); let colors = &["red"]; let dog = &["name=Fido", "age=2"]; diff --git a/core/lib/src/data/data.rs b/core/lib/src/data/data.rs index 3f393c5598..bbd2bb75a4 100644 --- a/core/lib/src/data/data.rs +++ b/core/lib/src/data/data.rs @@ -112,7 +112,7 @@ impl<'r> Data<'r> { /// /// async fn from_data(r: &'r Request<'_>, mut data: Data<'r>) -> Outcome<'r, Self> { /// if data.peek(2).await != b"hi" { - /// return Outcome::Forward((data, Status::NotFound)) + /// return Outcome::Forward((data, Status::BadRequest)) /// } /// /// /* .. */ diff --git a/core/lib/src/data/from_data.rs b/core/lib/src/data/from_data.rs index b3ac98c816..3eec28932d 100644 --- a/core/lib/src/data/from_data.rs +++ b/core/lib/src/data/from_data.rs @@ -9,27 +9,6 @@ use crate::outcome::{self, IntoOutcome, try_outcome, Outcome::*}; pub type Outcome<'r, T, E = >::Error> = outcome::Outcome, Status)>; -impl<'r, S, E> IntoOutcome, Status)> for Result { - type Error = Status; - type Forward = (Data<'r>, Status); - - #[inline] - fn into_outcome(self, status: Status) -> Outcome<'r, S, E> { - match self { - Ok(val) => Success(val), - Err(err) => Error((status, err)) - } - } - - #[inline] - fn or_forward(self, (data, status): (Data<'r>, Status)) -> Outcome<'r, S, E> { - match self { - Ok(val) => Success(val), - Err(_) => Forward((data, status)) - } - } -} - /// Trait implemented by data guards to derive a value from request body data. /// /// # Data Guards @@ -271,7 +250,7 @@ impl<'r, S, E> IntoOutcome, Status)> for Result /// // 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 Outcome::Forward((data, Status::NotFound)); +/// return Outcome::Forward((data, Status::UnsupportedMediaType)); /// } /// /// // Use a configured limit with name 'person' or fallback to default. @@ -343,7 +322,7 @@ impl<'r> FromData<'r> for Capped { async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> { let limit = req.limits().get("string").unwrap_or(Limits::STRING); - data.open(limit).into_string().await.into_outcome(Status::BadRequest) + data.open(limit).into_string().await.or_error(Status::BadRequest) } } @@ -406,7 +385,7 @@ impl<'r> FromData<'r> for Capped> { async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> { let limit = req.limits().get("bytes").unwrap_or(Limits::BYTES); - data.open(limit).into_bytes().await.into_outcome(Status::BadRequest) + data.open(limit).into_bytes().await.or_error(Status::BadRequest) } } diff --git a/core/lib/src/form/parser.rs b/core/lib/src/form/parser.rs index 7a9c4437be..b53e2a5008 100644 --- a/core/lib/src/form/parser.rs +++ b/core/lib/src/form/parser.rs @@ -35,7 +35,7 @@ impl<'r, 'i> Parser<'r, 'i> { let parser = match req.content_type() { Some(c) if c.is_form() => Self::from_form(req, data).await, Some(c) if c.is_form_data() => Self::from_multipart(req, data).await, - _ => return Outcome::Forward((data, Status::NotFound)), + _ => return Outcome::Forward((data, Status::UnsupportedMediaType)), }; match parser { diff --git a/core/lib/src/fs/server.rs b/core/lib/src/fs/server.rs index 8aac5c259b..45300db592 100644 --- a/core/lib/src/fs/server.rs +++ b/core/lib/src/fs/server.rs @@ -1,9 +1,10 @@ use std::path::{PathBuf, Path}; use crate::{Request, Data}; -use crate::http::{Method, uri::Segments, ext::IntoOwned}; +use crate::http::{Method, Status, uri::Segments, ext::IntoOwned}; use crate::route::{Route, Handler, Outcome}; -use crate::response::Redirect; +use crate::response::{Redirect, Responder}; +use crate::outcome::IntoOutcome; use crate::fs::NamedFile; /// Custom handler for serving static files. @@ -203,10 +204,10 @@ impl Handler for FileServer { }; if segments.is_empty() { - let file = NamedFile::open(&self.root).await.ok(); - return Outcome::from_or_forward(req, data, file); + let file = NamedFile::open(&self.root).await; + return file.respond_to(req).or_forward((data, Status::NotFound)); } else { - return Outcome::forward(data); + return Outcome::forward(data, Status::NotFound); } } @@ -224,18 +225,23 @@ impl Handler for FileServer { .expect("adding a trailing slash to a known good path => valid path") .into_owned(); - return Outcome::from_or_forward(req, data, Redirect::permanent(normal)); + return Redirect::permanent(normal) + .respond_to(req) + .or_forward((data, Status::InternalServerError)); } if !options.contains(Options::Index) { - return Outcome::forward(data); + return Outcome::forward(data, Status::NotFound); } - let index = NamedFile::open(p.join("index.html")).await.ok(); - Outcome::from_or_forward(req, data, index) + let index = NamedFile::open(p.join("index.html")).await; + index.respond_to(req).or_forward((data, Status::NotFound)) }, - Some(p) => Outcome::from_or_forward(req, data, NamedFile::open(p).await.ok()), - None => Outcome::forward(data), + Some(p) => { + let file = NamedFile::open(p).await; + file.respond_to(req).or_forward((data, Status::NotFound)) + } + None => Outcome::forward(data, Status::NotFound), } } } diff --git a/core/lib/src/fs/temp_file.rs b/core/lib/src/fs/temp_file.rs index 0f0ca9f5d0..fe969e7e9c 100644 --- a/core/lib/src/fs/temp_file.rs +++ b/core/lib/src/fs/temp_file.rs @@ -543,7 +543,7 @@ impl<'r> FromData<'r> for Capped> { } TempFile::from(req, data, None, req.content_type().cloned()).await - .into_outcome(Status::BadRequest) + .or_error(Status::BadRequest) } } diff --git a/core/lib/src/mtls.rs b/core/lib/src/mtls.rs index 07367bcf30..5910bb520c 100644 --- a/core/lib/src/mtls.rs +++ b/core/lib/src/mtls.rs @@ -20,6 +20,6 @@ impl<'r> FromRequest<'r> for Certificate<'r> { async fn from_request(req: &'r Request<'_>) -> Outcome { let certs = req.connection.client_certificates.as_ref().or_forward(Status::Unauthorized); let data = try_outcome!(try_outcome!(certs).chain_data().or_forward(Status::Unauthorized)); - Certificate::parse(data).into_outcome(Status::Unauthorized) + Certificate::parse(data).or_error(Status::Unauthorized) } } diff --git a/core/lib/src/outcome.rs b/core/lib/src/outcome.rs index 549e850910..88828b364c 100644 --- a/core/lib/src/outcome.rs +++ b/core/lib/src/outcome.rs @@ -90,6 +90,10 @@ use std::fmt; use yansi::{Paint, Color}; +use crate::{route, request, response}; +use crate::data::{self, Data, FromData}; +use crate::http::Status; + use self::Outcome::*; /// An enum representing success (`Success`), error (`Error`), or forwarding @@ -107,46 +111,6 @@ pub enum Outcome { Forward(F), } -/// Conversion trait from some type into an Outcome type. -pub trait IntoOutcome { - /// The type to use when returning an `Outcome::Error`. - type Error: Sized; - - /// The type to use when returning an `Outcome::Forward`. - type Forward: Sized; - - /// Converts `self` into an `Outcome`. If `self` represents a success, an - /// `Outcome::Success` is returned. Otherwise, an `Outcome::Error` is - /// returned with `error` as the inner value. - fn into_outcome(self, error: Self::Error) -> Outcome; - - /// Converts `self` into an `Outcome`. If `self` represents a success, an - /// `Outcome::Success` is returned. Otherwise, an `Outcome::Forward` is - /// returned with `forward` as the inner value. - fn or_forward(self, forward: Self::Forward) -> Outcome; -} - -impl IntoOutcome for Option { - type Error = E; - type Forward = F; - - #[inline] - fn into_outcome(self, error: E) -> Outcome { - match self { - Some(val) => Success(val), - None => Error(error) - } - } - - #[inline] - fn or_forward(self, forward: F) -> Outcome { - match self { - Some(val) => Success(val), - None => Forward(forward) - } - } -} - impl Outcome { /// Unwraps the Outcome, yielding the contents of a Success. /// @@ -651,15 +615,6 @@ impl Outcome { Outcome::Forward(v) => Err(v), } } - - #[inline] - fn formatting(&self) -> (Color, &'static str) { - match *self { - Success(..) => (Color::Green, "Success"), - Error(..) => (Color::Red, "Error"), - Forward(..) => (Color::Yellow, "Forward"), - } - } } impl<'a, S: Send + 'a, E: Send + 'a, F: Send + 'a> Outcome { @@ -755,15 +710,158 @@ crate::export! { } } +impl Outcome { + #[inline] + fn dbg_str(&self) -> &'static str { + match self { + Success(..) => "Success", + Error(..) => "Error", + Forward(..) => "Forward", + } + } + + #[inline] + fn color(&self) -> Color { + match self { + Success(..) => Color::Green, + Error(..) => Color::Red, + Forward(..) => Color::Yellow, + } + } +} + +pub(crate) struct Display<'a, 'r>(&'a route::Outcome<'r>); + +impl<'r> route::Outcome<'r> { + pub(crate) fn log_display(&self) -> Display<'_, 'r> { + impl fmt::Display for Display<'_, '_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", "Outcome: ".primary().bold())?; + + let color = self.0.color(); + match self.0 { + Success(r) => write!(f, "{}({})", "Success".paint(color), r.status().primary()), + Error(s) => write!(f, "{}({})", "Error".paint(color), s.primary()), + Forward((_, s)) => write!(f, "{}({})", "Forward".paint(color), s.primary()), + } + } + } + + Display(self) + } +} + impl fmt::Debug for Outcome { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Outcome::{}", self.formatting().1) + write!(f, "Outcome::{}", self.dbg_str()) } } impl fmt::Display for Outcome { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let (color, string) = self.formatting(); - write!(f, "{}", string.paint(color)) + write!(f, "{}", self.dbg_str().paint(self.color())) + } +} + +/// Conversion trait from some type into an Outcome type. +pub trait IntoOutcome { + /// The type to use when returning an `Outcome::Error`. + type Error: Sized; + + /// The type to use when returning an `Outcome::Forward`. + type Forward: Sized; + + /// Converts `self` into an `Outcome`. If `self` represents a success, an + /// `Outcome::Success` is returned. Otherwise, an `Outcome::Error` is + /// returned with `error` as the inner value. + fn or_error(self, error: Self::Error) -> Outcome; + + /// Converts `self` into an `Outcome`. If `self` represents a success, an + /// `Outcome::Success` is returned. Otherwise, an `Outcome::Forward` is + /// returned with `forward` as the inner value. + fn or_forward(self, forward: Self::Forward) -> Outcome; +} + +impl IntoOutcome> for Option { + type Error = E; + type Forward = F; + + #[inline] + fn or_error(self, error: E) -> Outcome { + match self { + Some(val) => Success(val), + None => Error(error) + } + } + + #[inline] + fn or_forward(self, forward: F) -> Outcome { + match self { + Some(val) => Success(val), + None => Forward(forward) + } + } +} + +impl<'r, T: FromData<'r>> IntoOutcome> for Result { + type Error = Status; + type Forward = (Data<'r>, Status); + + #[inline] + fn or_error(self, error: Status) -> data::Outcome<'r, T> { + match self { + Ok(val) => Success(val), + Err(err) => Error((error, err)) + } + } + + #[inline] + fn or_forward(self, (data, forward): (Data<'r>, Status)) -> data::Outcome<'r, T> { + match self { + Ok(val) => Success(val), + Err(_) => Forward((data, forward)) + } + } +} + +impl IntoOutcome> for Result { + type Error = Status; + type Forward = Status; + + #[inline] + fn or_error(self, error: Status) -> request::Outcome { + match self { + Ok(val) => Success(val), + Err(err) => Error((error, err)) + } + } + + #[inline] + fn or_forward(self, status: Status) -> request::Outcome { + match self { + Ok(val) => Success(val), + Err(_) => Forward(status) + } + } +} + +impl<'r, 'o: 'r> IntoOutcome> for response::Result<'o> { + type Error = (); + type Forward = (Data<'r>, Status); + + #[inline] + fn or_error(self, _: ()) -> route::Outcome<'r> { + match self { + Ok(val) => Success(val), + Err(status) => Error(status), + } + } + + #[inline] + fn or_forward(self, (data, forward): (Data<'r>, Status)) -> route::Outcome<'r> { + match self { + Ok(val) => Success(val), + Err(_) => Forward((data, forward)) + } } } diff --git a/core/lib/src/request/from_request.rs b/core/lib/src/request/from_request.rs index 3d048e8541..203c7d490f 100644 --- a/core/lib/src/request/from_request.rs +++ b/core/lib/src/request/from_request.rs @@ -3,36 +3,14 @@ use std::fmt::Debug; use std::net::{IpAddr, SocketAddr}; use crate::{Request, Route}; -use crate::outcome::{self, IntoOutcome}; -use crate::outcome::Outcome::*; +use crate::outcome::{self, Outcome::*}; -use crate::http::{Status, ContentType, Accept, Method, CookieJar}; use crate::http::uri::{Host, Origin}; +use crate::http::{Status, ContentType, Accept, Method, CookieJar}; /// Type alias for the `Outcome` of a `FromRequest` conversion. pub type Outcome = outcome::Outcome; -impl IntoOutcome for Result { - type Error = Status; - type Forward = Status; - - #[inline] - fn into_outcome(self, status: Status) -> Outcome { - match self { - Ok(val) => Success(val), - Err(err) => Error((status, err)) - } - } - - #[inline] - fn or_forward(self, status: Status) -> Outcome { - match self { - Ok(val) => Success(val), - Err(_) => Forward(status) - } - } -} - /// Trait implemented by request guards to derive a value from incoming /// requests. /// @@ -136,7 +114,8 @@ impl IntoOutcome for Result { /// * **&Host** /// /// Extracts the [`Host`] from the incoming request, if it exists. See -/// [`Request::host()`] for details. +/// [`Request::host()`] for details. If it does not exist, the request is +/// forwarded with a 500 Internal Server Error status. /// /// * **&Route** /// @@ -162,23 +141,30 @@ impl IntoOutcome for Result { /// /// _This implementation always returns successfully._ /// -/// * **ContentType** +/// * **&ContentType** /// /// Extracts the [`ContentType`] from the incoming request via /// [`Request::content_type()`]. If the request didn't specify a -/// Content-Type, the request is forwarded with a 404 Not Found status. +/// Content-Type, the request is forwarded with a 500 Internal Server Error +/// status. +/// +/// * **&ContentType** /// -/// * **IpAddr** +/// Extracts the [`Accept`] from the incoming request via +/// [`Request::accept()`]. If the request didn't specify an `Accept`, the +/// request is forwarded with a 500 Internal Server Error status. +/// +/// * ***IpAddr** /// /// Extracts the client ip address of the incoming request as an [`IpAddr`] /// via [`Request::client_ip()`]. If the client's IP address is not known, -/// the request is forwarded with a 404 Not Found status. +/// the request is forwarded with a 500 Internal Server Error status. /// /// * **SocketAddr** /// /// Extracts the remote address of the incoming request as a [`SocketAddr`] /// via [`Request::remote()`]. If the remote address is not known, the -/// request is forwarded with a 404 Not Found status. +/// request is forwarded with a 500 Internal Server Error status. /// /// * **Option<T>** _where_ **T: FromRequest** /// @@ -422,7 +408,7 @@ impl<'r> FromRequest<'r> for &'r Host<'r> { async fn from_request(request: &'r Request<'_>) -> Outcome { match request.host() { Some(host) => Success(host), - None => Forward(Status::NotFound) + None => Forward(Status::InternalServerError) } } } @@ -455,7 +441,7 @@ impl<'r> FromRequest<'r> for &'r Accept { async fn from_request(request: &'r Request<'_>) -> Outcome { match request.accept() { Some(accept) => Success(accept), - None => Forward(Status::NotFound) + None => Forward(Status::InternalServerError) } } } @@ -467,7 +453,7 @@ impl<'r> FromRequest<'r> for &'r ContentType { async fn from_request(request: &'r Request<'_>) -> Outcome { match request.content_type() { Some(content_type) => Success(content_type), - None => Forward(Status::NotFound) + None => Forward(Status::InternalServerError) } } } @@ -479,7 +465,7 @@ impl<'r> FromRequest<'r> for IpAddr { async fn from_request(request: &'r Request<'_>) -> Outcome { match request.client_ip() { Some(addr) => Success(addr), - None => Forward(Status::NotFound) + None => Forward(Status::InternalServerError) } } } @@ -491,7 +477,7 @@ impl<'r> FromRequest<'r> for SocketAddr { async fn from_request(request: &'r Request<'_>) -> Outcome { match request.remote() { Some(addr) => Success(addr), - None => Forward(Status::NotFound) + None => Forward(Status::InternalServerError) } } } diff --git a/core/lib/src/response/flash.rs b/core/lib/src/response/flash.rs index 187223ab4f..688e6d0e2b 100644 --- a/core/lib/src/response/flash.rs +++ b/core/lib/src/response/flash.rs @@ -259,7 +259,7 @@ impl<'r> FromRequest<'r> for FlashMessage<'r> { Ok(i) if i <= kv.len() => Ok(Flash::named(&kv[..i], &kv[i..], req)), _ => Err(()) } - }).into_outcome(Status::BadRequest) + }).or_error(Status::BadRequest) } } diff --git a/core/lib/src/route/handler.rs b/core/lib/src/route/handler.rs index d139942b62..e29be6d570 100644 --- a/core/lib/src/route/handler.rs +++ b/core/lib/src/route/handler.rs @@ -218,31 +218,6 @@ impl<'r, 'o: 'r> Outcome<'o> { } } - /// Return the `Outcome` of response to `req` from `responder`. - /// - /// If the responder returns `Ok`, an outcome of `Success` is returned with - /// the response. If the responder returns `Err`, an outcome of `Forward` - /// with a status of `404 Not Found` is returned. - /// - /// # Example - /// - /// ```rust - /// use rocket::{Request, Data, route}; - /// - /// fn str_responder<'r>(req: &'r Request, data: Data<'r>) -> route::Outcome<'r> { - /// route::Outcome::from_or_forward(req, data, "Hello, world!") - /// } - /// ``` - #[inline] - pub fn from_or_forward(req: &'r Request<'_>, data: Data<'r>, responder: R) -> Outcome<'r> - where R: Responder<'r, 'o> - { - match responder.respond_to(req) { - Ok(response) => Outcome::Success(response), - Err(_) => Outcome::Forward((data, Status::NotFound)) - } - } - /// Return an `Outcome` of `Error` with the status code `code`. This is /// equivalent to `Outcome::Error(code)`. /// @@ -263,8 +238,8 @@ impl<'r, 'o: 'r> Outcome<'o> { Outcome::Error(code) } - /// Return an `Outcome` of `Forward` with the data `data`. This is - /// equivalent to `Outcome::Forward((data, Status::NotFound))`. + /// Return an `Outcome` of `Forward` with the data `data` and status + /// `status`. This is equivalent to `Outcome::Forward((data, status))`. /// /// This method exists to be used during manual routing. /// @@ -272,14 +247,15 @@ impl<'r, 'o: 'r> Outcome<'o> { /// /// ```rust /// use rocket::{Request, Data, route}; + /// use rocket::http::Status; /// /// fn always_forward<'r>(_: &'r Request, data: Data<'r>) -> route::Outcome<'r> { - /// route::Outcome::forward(data) + /// route::Outcome::forward(data, Status::InternalServerError) /// } /// ``` #[inline(always)] - pub fn forward(data: Data<'r>) -> Outcome<'r> { - Outcome::Forward((data, Status::NotFound)) + pub fn forward(data: Data<'r>, status: Status) -> Outcome<'r> { + Outcome::Forward((data, status)) } } diff --git a/core/lib/src/server.rs b/core/lib/src/server.rs index e874b0ff6b..800c9dbe63 100644 --- a/core/lib/src/server.rs +++ b/core/lib/src/server.rs @@ -334,7 +334,7 @@ impl Rocket { // Check if the request processing completed (Some) or if the // request needs to be forwarded. If it does, continue the loop // (None) to try again. - info_!("{} {}", "Outcome:".primary().bold(), outcome); + info_!("{}", outcome.log_display()); match outcome { o@Outcome::Success(_) | o@Outcome::Error(_) => return o, Outcome::Forward(forwarded) => (data, status) = forwarded, diff --git a/examples/error-handling/src/tests.rs b/examples/error-handling/src/tests.rs index db5ac18d7f..585913e148 100644 --- a/examples/error-handling/src/tests.rs +++ b/examples/error-handling/src/tests.rs @@ -46,7 +46,15 @@ fn forced_error() { fn test_hello_invalid_age() { let client = Client::tracked(super::rocket()).unwrap(); - for path in &["Ford/-129", "Trillian/128", "foo/bar/baz"] { + for path in &["Ford/-129", "Trillian/128"] { + let request = client.get(format!("/hello/{}", path)); + let expected = super::default_catcher(Status::UnprocessableEntity, request.inner()); + let response = request.dispatch(); + assert_eq!(response.status(), Status::UnprocessableEntity); + assert_eq!(response.into_string().unwrap(), expected.1); + } + + for path in &["foo/bar/baz"] { let request = client.get(format!("/hello/{}", path)); let expected = super::hello_not_found(request.inner()); let response = request.dispatch(); @@ -59,7 +67,15 @@ fn test_hello_invalid_age() { fn test_hello_sergio() { let client = Client::tracked(super::rocket()).unwrap(); - for path in &["oops", "-129", "foo/bar", "/foo/bar/baz"] { + for path in &["oops", "-129"] { + let request = client.get(format!("/hello/Sergio/{}", path)); + let expected = super::sergio_error(); + let response = request.dispatch(); + assert_eq!(response.status(), Status::UnprocessableEntity); + assert_eq!(response.into_string().unwrap(), expected); + } + + for path in &["foo/bar", "/foo/bar/baz"] { let request = client.get(format!("/hello/Sergio/{}", path)); let expected = super::sergio_error(); let response = request.dispatch(); diff --git a/examples/hello/src/tests.rs b/examples/hello/src/tests.rs index fd5b628d96..130ff0f31d 100644 --- a/examples/hello/src/tests.rs +++ b/examples/hello/src/tests.rs @@ -62,10 +62,10 @@ fn wave() { let response = client.get(uri).dispatch(); assert_eq!(response.into_string().unwrap(), expected); - for bad_age in &["1000", "-1", "bird", "?"] { + for bad_age in &["1000", "-1", "bird"] { let bad_uri = format!("/wave/{}/{}", name, bad_age); let response = client.get(bad_uri).dispatch(); - assert_eq!(response.status(), Status::NotFound); + assert_eq!(response.status(), Status::UnprocessableEntity); } } } diff --git a/examples/manual-routing/src/main.rs b/examples/manual-routing/src/main.rs index 2da4c42ad2..e4a21620f0 100644 --- a/examples/manual-routing/src/main.rs +++ b/examples/manual-routing/src/main.rs @@ -9,7 +9,7 @@ use rocket::outcome::{try_outcome, IntoOutcome}; use rocket::tokio::fs::File; fn forward<'r>(_req: &'r Request, data: Data<'r>) -> route::BoxFuture<'r> { - Box::pin(async move { route::Outcome::forward(data) }) + Box::pin(async move { route::Outcome::forward(data, Status::NotFound) }) } fn hi<'r>(req: &'r Request, _: Data<'r>) -> route::BoxFuture<'r> { @@ -27,7 +27,7 @@ fn name<'r>(req: &'r Request, _: Data<'r>) -> route::BoxFuture<'r> { fn echo_url<'r>(req: &'r Request, _: Data<'r>) -> route::BoxFuture<'r> { let param_outcome = req.param::<&str>(1) .and_then(Result::ok) - .into_outcome(Status::BadRequest); + .or_error(Status::BadRequest); Box::pin(async move { route::Outcome::from(req, try_outcome!(param_outcome)) diff --git a/examples/serialization/src/tests.rs b/examples/serialization/src/tests.rs index 1a7846ba11..8a46c13dcc 100644 --- a/examples/serialization/src/tests.rs +++ b/examples/serialization/src/tests.rs @@ -34,8 +34,7 @@ fn json_bad_get_put() { // Try to get a message with an invalid ID. let res = client.get("/json/hi").header(ContentType::JSON).dispatch(); - assert_eq!(res.status(), Status::NotFound); - assert!(res.into_string().unwrap().contains("error")); + assert_eq!(res.status(), Status::UnprocessableEntity); // Try to put a message without a proper body. let res = client.put("/json/80").header(ContentType::JSON).dispatch(); @@ -134,5 +133,5 @@ fn uuid() { } let res = client.get("/people/not-a-uuid").dispatch(); - assert_eq!(res.status(), Status::NotFound); + assert_eq!(res.status(), Status::UnprocessableEntity); } From 48d1b82e840348842cc0d089428a02ba9b982bef Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Wed, 1 Nov 2023 01:16:28 -0500 Subject: [PATCH 18/22] Qualm various emerging unused warnings. --- core/codegen/src/attribute/route/mod.rs | 1 + core/http/src/tls/mtls.rs | 1 - examples/cookies/src/message.rs | 2 +- examples/cookies/src/session.rs | 2 +- examples/forms/src/main.rs | 2 ++ examples/upgrade/src/main.rs | 3 ++- 6 files changed, 7 insertions(+), 4 deletions(-) diff --git a/core/codegen/src/attribute/route/mod.rs b/core/codegen/src/attribute/route/mod.rs index c2e3043ab6..3b72f470a7 100644 --- a/core/codegen/src/attribute/route/mod.rs +++ b/core/codegen/src/attribute/route/mod.rs @@ -225,6 +225,7 @@ fn internal_uri_macro_decl(route: &Route) -> TokenStream { } #[doc(hidden)] + #[allow(unused)] pub use #inner_macro_name as #macro_name; } } diff --git a/core/http/src/tls/mtls.rs b/core/http/src/tls/mtls.rs index 45ea38fc48..7a7cd1697c 100644 --- a/core/http/src/tls/mtls.rs +++ b/core/http/src/tls/mtls.rs @@ -4,7 +4,6 @@ pub mod oid { //! [`der-parser`](https://docs.rs/der-parser/7). pub use x509_parser::oid_registry::*; - pub use x509_parser::der_parser::oid::*; pub use x509_parser::objects::*; } diff --git a/examples/cookies/src/message.rs b/examples/cookies/src/message.rs index d4896adbf6..06a33a0085 100644 --- a/examples/cookies/src/message.rs +++ b/examples/cookies/src/message.rs @@ -1,6 +1,6 @@ use rocket::form::Form; use rocket::response::Redirect; -use rocket::http::{Cookie, CookieJar}; +use rocket::http::CookieJar; use rocket_dyn_templates::{Template, context}; #[macro_export] diff --git a/examples/cookies/src/session.rs b/examples/cookies/src/session.rs index d66a5911aa..11bf55d149 100644 --- a/examples/cookies/src/session.rs +++ b/examples/cookies/src/session.rs @@ -1,7 +1,7 @@ use rocket::outcome::IntoOutcome; use rocket::request::{self, FlashMessage, FromRequest, Request}; use rocket::response::{Redirect, Flash}; -use rocket::http::{Cookie, CookieJar, Status}; +use rocket::http::{CookieJar, Status}; use rocket::form::Form; use rocket_dyn_templates::{Template, context}; diff --git a/examples/forms/src/main.rs b/examples/forms/src/main.rs index 97c13e728b..a908eb4705 100644 --- a/examples/forms/src/main.rs +++ b/examples/forms/src/main.rs @@ -11,7 +11,9 @@ use rocket_dyn_templates::Template; struct Password<'v> { #[field(validate = len(6..))] #[field(validate = eq(self.second))] + #[allow(unused)] first: &'v str, + #[allow(unused)] #[field(validate = eq(self.first))] second: &'v str, } diff --git a/examples/upgrade/src/main.rs b/examples/upgrade/src/main.rs index 943ff07098..7526b5b155 100644 --- a/examples/upgrade/src/main.rs +++ b/examples/upgrade/src/main.rs @@ -16,7 +16,8 @@ fn echo_stream(ws: ws::WebSocket) -> ws::Stream!['static] { fn echo_channel(ws: ws::WebSocket) -> ws::Channel<'static> { // This is entirely optional. Change default configuration. let ws = ws.config(ws::Config { - max_send_queue: Some(5), + // set max message size to 3MiB + max_message_size: Some(3 << 20), ..Default::default() }); From fa0c778276b381d26da1e8807a6de7a3427a3262 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Wed, 1 Nov 2023 12:08:26 -0500 Subject: [PATCH 19/22] Set 'SameSite' to 'Lax' on removal cookies. This avoids needless warnings from certain browsers. --- core/lib/src/cookies.rs | 75 ++++++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 19 deletions(-) diff --git a/core/lib/src/cookies.rs b/core/lib/src/cookies.rs index 3e7df583e1..368172cd61 100644 --- a/core/lib/src/cookies.rs +++ b/core/lib/src/cookies.rs @@ -350,17 +350,24 @@ impl<'a> CookieJar<'a> { self.ops.lock().push(Op::Add(cookie, true)); } - /// Removes `cookie` from this collection and generates a "removal" cookies + /// Removes `cookie` from this collection and generates a "removal" cookie /// to send to the client on response. A "removal" cookie is a cookie that /// has the same name as the original cookie but has an empty value, a /// max-age of 0, and an expiration date far in the past. /// - /// **Note: For correctness, `cookie` must contain the same `path` and - /// `domain` as the cookie that was initially set. Failure to provide the - /// initial `path` and `domain` will result in cookies that are not properly - /// removed. For convenience, if a path is not set on `cookie`, the `"/"` - /// path will automatically be set.** + /// **For successful removal, `cookie` must contain the same `path` and + /// `domain` as the cookie that was originally set. The cookie will fail to + /// be deleted if any other `path` and `domain` are provided. For + /// convenience, a path of `"/"` is automatically set when one is not + /// specified.** The full list of defaults when corresponding values aren't + /// specified is: /// + /// * `path`: `"/"` + /// * `SameSite`: `Lax` + /// + /// Note: a default setting of `Lax` for `SameSite` carries no + /// security implications: the removal cookie has expired, so it is never + /// transferred to any origin. /// /// # Example /// @@ -370,47 +377,62 @@ impl<'a> CookieJar<'a> { /// /// #[get("/")] /// fn handler(jar: &CookieJar<'_>) { - /// // Rocket will set `path` to `/`. + /// // `path` and `SameSite` are set to defaults (`/` and `Lax`) /// jar.remove("name"); /// /// // Use a custom-built cookie to set a custom path. /// jar.remove(Cookie::build("name").path("/login")); + /// + /// // Use a custom-built cookie to set a custom path and domain. + /// jar.remove(Cookie::build("id").path("/guide").domain("rocket.rs")); /// } /// ``` pub fn remove>>(&self, cookie: C) { let mut cookie = cookie.into(); - if cookie.path().is_none() { - cookie.set_path("/"); - } - + Self::set_removal_defaults(&mut cookie); self.ops.lock().push(Op::Remove(cookie, false)); } /// Removes the private `cookie` from the collection. /// - /// For correct removal, the passed in `cookie` must contain the same `path` - /// and `domain` as the cookie that was initially set. If a path is not set - /// on `cookie`, the `"/"` path will automatically be set. + /// **For successful removal, `cookie` must contain the same `path` and + /// `domain` as the cookie that was originally set. The cookie will fail to + /// be deleted if any other `path` and `domain` are provided. For + /// convenience, a path of `"/"` is automatically set when one is not + /// specified.** The full list of defaults when corresponding values aren't + /// specified is: + /// + /// * `path`: `"/"` + /// * `SameSite`: `Lax` + /// + /// Note: a default setting of `Lax` for `SameSite` carries no + /// security implications: the removal cookie has expired, so it is never + /// transferred to any origin. /// /// # Example /// /// ```rust /// # #[macro_use] extern crate rocket; - /// use rocket::http::CookieJar; + /// use rocket::http::{CookieJar, Cookie}; /// /// #[get("/")] /// fn handler(jar: &CookieJar<'_>) { + /// // `path` and `SameSite` are set to defaults (`/` and `Lax`) /// jar.remove_private("name"); + /// + /// // Use a custom-built cookie to set a custom path. + /// jar.remove_private(Cookie::build("name").path("/login")); + /// + /// // Use a custom-built cookie to set a custom path and domain. + /// let cookie = Cookie::build("id").path("/guide").domain("rocket.rs"); + /// jar.remove_private(cookie); /// } /// ``` #[cfg(feature = "secrets")] #[cfg_attr(nightly, doc(cfg(feature = "secrets")))] pub fn remove_private>>(&self, cookie: C) { let mut cookie = cookie.into(); - if cookie.path().is_none() { - cookie.set_path("/"); - } - + Self::set_removal_defaults(&mut cookie); self.ops.lock().push(Op::Remove(cookie, true)); } @@ -508,6 +530,21 @@ impl<'a> CookieJar<'a> { } } + /// For each property below, this method checks if there is a provided value + /// and if there is none, sets a default value. Default values are: + /// + /// * `path`: `"/"` + /// * `SameSite`: `Lax` + fn set_removal_defaults(cookie: &mut Cookie<'static>) { + if cookie.path().is_none() { + cookie.set_path("/"); + } + + if cookie.same_site().is_none() { + cookie.set_same_site(SameSite::Lax); + } + } + /// For each property mentioned below, this method checks if there is a /// provided value and if there is none, sets a default value. Default /// values are: From 1df854e13a07f1f4f075bfd885a4a5da55d303be Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Wed, 1 Nov 2023 12:08:57 -0500 Subject: [PATCH 20/22] Add a "clear message" button to cookies example. --- examples/cookies/src/message.rs | 11 +++++-- examples/cookies/templates/message.html.hbs | 32 +++++++++++++++------ 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/examples/cookies/src/message.rs b/examples/cookies/src/message.rs index 06a33a0085..a1d8c9b033 100644 --- a/examples/cookies/src/message.rs +++ b/examples/cookies/src/message.rs @@ -16,12 +16,19 @@ fn submit(cookies: &CookieJar<'_>, message: Form<&str>) -> Redirect { Redirect::to(uri!(index)) } +#[delete("/")] +fn delete(cookies: &CookieJar<'_>) -> Redirect { + cookies.remove("message"); + Redirect::to(uri!(index)) +} + #[get("/")] fn index(cookies: &CookieJar<'_>) -> Template { let message = cookies.get("message").map(|c| c.value()); - Template::render("message", context! { message }) + let present = cookies.get("message").is_some(); + Template::render("message", context! { present, message }) } pub fn routes() -> Vec { - routes![submit, index] + routes![index, submit, delete] } diff --git a/examples/cookies/templates/message.html.hbs b/examples/cookies/templates/message.html.hbs index 7dcec374c7..df724332d5 100644 --- a/examples/cookies/templates/message.html.hbs +++ b/examples/cookies/templates/message.html.hbs @@ -7,18 +7,32 @@

Rocket Cookie Message

- {{#if message }} -

{{message}}

- {{else}} -

No message yet.

- {{/if}} - - -

+

+ {{#if present}} + {{#if message}} + Message: {{message}} + {{else}} + Message: [empty message] + {{/if}} + {{else}} + No message yet. + {{/if}} +

+ + + + + +
+
+

+ + +

+ Home From f14f93afa7e62bd92068e9d3f425f3b2fd75e43f Mon Sep 17 00:00:00 2001 From: Benjamin B <7598058+BBlackwo@users.noreply.github.com> Date: Thu, 2 Nov 2023 09:14:15 +1100 Subject: [PATCH 21/22] Fix typo in pastebin tutorial: 'route' -> 'wrote'. --- site/guide/10-pastebin-tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/guide/10-pastebin-tutorial.md b/site/guide/10-pastebin-tutorial.md index 875c72ae3c..6cdda784c9 100644 --- a/site/guide/10-pastebin-tutorial.md +++ b/site/guide/10-pastebin-tutorial.md @@ -384,7 +384,7 @@ Now that we can retrieve pastes safely, it's time to actually store them. We'll write an `upload` route that, according to our design, takes a paste's contents and writes them to a file with a randomly generated ID inside of the `upload/` directory. It'll return a URL to the client for the paste corresponding to the -`retrieve` route we just route. +`retrieve` route we just wrote. ### Streaming Data From 15637186bae98eb6b3a907d5bdade4fbacdc4e59 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Wed, 1 Nov 2023 18:48:41 -0500 Subject: [PATCH 22/22] Document '&[u8]' form/data guard, limits. --- core/lib/src/data/limits.rs | 4 +++- core/lib/src/form/from_form.rs | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/core/lib/src/data/limits.rs b/core/lib/src/data/limits.rs index 9606304e38..535d843aad 100644 --- a/core/lib/src/data/limits.rs +++ b/core/lib/src/data/limits.rs @@ -59,8 +59,10 @@ use crate::http::uncased::Uncased; /// | `data-form` | 2MiB | [`Form`] | entire data-based form | /// | `file` | 1MiB | [`TempFile`] | [`TempFile`] data guard or form field | /// | `file/$ext` | _N/A_ | [`TempFile`] | file form field with extension `$ext` | -/// | `string` | 8KiB | [`String`] | data guard or data form field | +/// | `string` | 8KiB | [`String`] | data guard or form field | +/// | `string` | 8KiB | [`&str`] | data guard or form field | /// | `bytes` | 8KiB | [`Vec`] | data guard | +/// | `bytes` | 8KiB | [`&[u8]`] | data guard or form field | /// | `json` | 1MiB | [`Json`] | JSON data and form payloads | /// | `msgpack` | 1MiB | [`MsgPack`] | MessagePack data and form payloads | /// diff --git a/core/lib/src/form/from_form.rs b/core/lib/src/form/from_form.rs index f44be8cf99..347b62d80c 100644 --- a/core/lib/src/form/from_form.rs +++ b/core/lib/src/form/from_form.rs @@ -116,11 +116,12 @@ use crate::http::uncased::AsUncased; /// | _nonzero_ int | _inherit_ | **no default** | No | Yes | `NonZero{I,U}{size,8,16,32,64,128}` | /// | float | _inherit_ | **no default** | No | Yes | `f{32,64}` | /// | `&str` | _inherit_ | **no default** | Yes | Yes | Percent-decoded. Data limit `string` applies. | +/// | `&[u8]` | _inherit_ | **no default** | Yes | Yes | Raw bytes. Data limit `bytes` applies. | /// | `String` | _inherit_ | **no default** | Yes | Yes | Exactly `&str`, but owned. Prefer `&str`. | /// | IP Address | _inherit_ | **no default** | No | Yes | [`IpAddr`], [`Ipv4Addr`], [`Ipv6Addr`] | /// | Socket Address | _inherit_ | **no default** | No | Yes | [`SocketAddr`], [`SocketAddrV4`], [`SocketAddrV6`] | /// | [`TempFile`] | _inherit_ | **no default** | Yes | Yes | Data limits apply. See [`TempFile`]. | -/// | [`Capped`] | _inherit_ | **no default** | Yes | Yes | `C` is `&str`, `String`, `&[u8]` or `TempFile`. | +/// | [`Capped`] | _inherit_ | **no default** | Yes | Yes | `C` is `&str`, `String`, `&[u8]` or `TempFile`. | /// | [`time::Date`] | _inherit_ | **no default** | No | Yes | `%F` (`YYYY-MM-DD`). HTML "date" input. | /// | [`time::DateTime`] | _inherit_ | **no default** | No | Yes | `%FT%R` or `%FT%T` (`YYYY-MM-DDTHH:MM[:SS]`) | /// | [`time::Time`] | _inherit_ | **no default** | No | Yes | `%R` or `%T` (`HH:MM[:SS]`) |