Skip to content

Commit

Permalink
Clean up initial implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
the10thWiz committed Nov 22, 2024
1 parent aa845ad commit 0b72867
Show file tree
Hide file tree
Showing 8 changed files with 51 additions and 48 deletions.
1 change: 0 additions & 1 deletion core/lib/src/catcher/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ pub type BoxFuture<'r, T = Result<'r>> = futures::future::BoxFuture<'r, T>;
/// directly as the parameter to `rocket.register("/", )`.
/// 3. Unlike static-function-based handlers, this custom handler can make use
/// of internal state.
// TODO: Typed: Docs
#[crate::async_trait]
pub trait Handler: Cloneable + Send + Sync + 'static {
/// Called by Rocket when an error with `status` for a given `Request`
Expand Down
41 changes: 6 additions & 35 deletions core/lib/src/catcher/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ mod sealed {

/// This is the core of typed catchers. If an error type (returned by
/// FromParam, FromRequest, FromForm, FromData, or Responder) implements
/// this trait, it can be caught by a typed catcher. (TODO) This trait
/// this trait, it can be caught by a typed catcher. This trait
/// can be derived.
pub trait TypedError<'r>: AsAny<Inv<'r>> + Send + Sync + 'r {
/// Generates a default response for this type (or forwards to a default catcher)
Expand All @@ -46,21 +46,14 @@ pub trait TypedError<'r>: AsAny<Inv<'r>> + Send + Sync + 'r {
/// A descriptive name of this error type. Defaults to the type name.
fn name(&self) -> &'static str { std::any::type_name::<Self>() }

// /// The error that caused this error. Defaults to None.
// ///
// /// # Warning
// /// A typed catcher will not attempt to follow the source of an error
// /// more than (TODO: exact number) 5 times.
// fn source(&'r self) -> Option<&'r (dyn TypedError<'r> + 'r)> { None }

// TODO: Typed: need to support case where there are multiple errors
/// The error that caused this error. Defaults to None. Each source
/// should only be returned for one index - this method will be called
/// with indicies starting with 0, and increasing until it returns None.
/// with indicies starting with 0, and increasing until it returns None,
/// or reaches 5.
///
/// # Warning
/// A typed catcher will not attempt to follow the source of an error
/// more than (TODO: exact number) 5 times.
/// more than 5 times.
fn source(&'r self, _idx: usize) -> Option<&'r (dyn TypedError<'r> + 'r)> { None }

/// Status code
Expand All @@ -76,7 +69,6 @@ impl<'r> TypedError<'r> for Status {
}

fn name(&self) -> &'static str {
// TODO: Status generally shouldn't be caught
"<Status>"
}

Expand All @@ -96,39 +88,18 @@ impl AsStatus for Box<dyn TypedError<'_> + '_> {
self.status()
}
}
// TODO: Typed: update transient to make the possible.
// impl<'r, R: TypedError<'r> + Transient> TypedError<'r> for (Status, R)
// where R::Transience: CanTranscendTo<Inv<'r>>
// {
// fn respond_to(&self, request: &'r Request<'_>) -> Result<Response<'r>, Status> {
// self.1.respond_to(request)
// }

// fn name(&self) -> &'static str {
// self.1.name()
// }

// fn source(&'r self) -> Option<&'r (dyn TypedError<'r> + 'r)> {
// Some(&self.1)
// }

// fn status(&self) -> Status {
// self.0
// }
// }

impl<'r, A: TypedError<'r> + Transient, B: TypedError<'r> + Transient> TypedError<'r> for (A, B)
where A::Transience: CanTranscendTo<Inv<'r>>,
B::Transience: CanTranscendTo<Inv<'r>>,
// (A, B): Transient,
// <(A, B) as Transient>::Transience: CanTranscendTo<Inv<'r>>,
{
fn respond_to(&self, request: &'r Request<'_>) -> Result<Response<'r>, Status> {
self.0.respond_to(request).or_else(|_| self.1.respond_to(request))
}

fn name(&self) -> &'static str {
// TODO: Typed: Should indicate that the
// TODO: This should make it more clear that both `A` and `B` work, but
// would likely require const concatenation.
std::any::type_name::<(A, B)>()
}

Expand Down
10 changes: 7 additions & 3 deletions core/lib/src/lifecycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ impl Rocket<Orbit> {
) -> route::Outcome<'r> {
// Go through all matching routes until we fail or succeed or run out of
// routes to try, in which case we forward with the last status.
let mut status: Box<dyn TypedError<'r> + 'r> = Box::new(Status::NotFound);
let mut error: Box<dyn TypedError<'r> + 'r> = Box::new(Status::NotFound);
for route in self.router.route(request) {
// Retrieve and set the requests parameters.
route.trace_info();
Expand All @@ -243,11 +243,11 @@ impl Rocket<Orbit> {
outcome.trace_info();
match outcome {
o @ Outcome::Success(_) | o @ Outcome::Error(_) => return o,
Outcome::Forward(forwarded) => (data, status) = forwarded,
Outcome::Forward(forwarded) => (data, error) = forwarded,
}
}

Outcome::Forward((data, status))
Outcome::Forward((data, error))
}

// Invokes the catcher for `status`. Returns the response on success.
Expand Down Expand Up @@ -329,6 +329,7 @@ impl Rocket<Orbit> {
/// 5 calls deep
/// * Matching Status, but not Type
/// * Default handler
/// * Error type's default handler
/// * Rocket's default
///
/// The handler selected to be invoked is the one with the lowest rank.
Expand Down Expand Up @@ -361,6 +362,9 @@ impl Rocket<Orbit> {
.await
.map(|result| result.map_err(Some))
.unwrap_or_else(|| Err(None))
// TODO: Typed: should this be run in a `catch_unwind` context?
} else if let Ok(res) = error.respond_to(req) {
Ok(res)
} else {
info!(name: "catcher", name = "rocket::default", "uri.base" = "/",
code = error.status().code, "no registered catcher: using Rocket default");
Expand Down
31 changes: 28 additions & 3 deletions core/lib/src/request/from_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,15 @@ pub type Outcome<S, E> = outcome::Outcome<S, E, E>;
/// the value for the corresponding parameter. As long as all other guards
/// succeed, the request will be handled.
///
/// * **Error**(Status, E)
/// * **Error**(E)
///
/// If the `Outcome` is [`Error`], the request will fail with the given
/// status code and error. The designated error [`Catcher`](crate::Catcher)
/// will be used to respond to the request. Note that users can request types
/// of `Result<S, E>` and `Option<S>` to catch `Error`s and retrieve the
/// error value.
///
/// * **Forward**(Status)
/// * **Forward**(E)
///
/// If the `Outcome` is [`Forward`], the request will be forwarded to the next
/// matching route until either one succeeds or there are no further matching
Expand Down Expand Up @@ -242,6 +242,32 @@ pub type Outcome<S, E> = outcome::Outcome<S, E, E>;
/// }
/// ```
///
/// ## Errors
///
/// When a request guard fails, the error type can be caught using a catcher. A catcher
/// for the above example might look something like this:
///
/// ```rust
/// # #[macro_use] extern crate rocket;
/// # use rocket::http::Status;
/// # use rocket::request::{self, Outcome, Request, FromRequest};
/// # #[derive(Debug, TypedError)]
/// # #[error(status = 400)]
/// # enum ApiKeyError {
/// # Missing,
/// # Invalid,
/// # }
/// #[catch(400, error = "<e>")]
/// fn catch_api_key_error(e: &ApiKeyError) -> &'static str {
/// match e {
/// ApiKeyError::Missing => "Api key required",
/// ApiKeyError::Invalid => "Api key is invalid",
/// }
/// }
/// ```
///
/// See [typed catchers](crate::catch) for more information.
///
/// # Request-Local State
///
/// Request guards that perform expensive operations, such as those that query a
Expand Down Expand Up @@ -379,7 +405,6 @@ pub type Outcome<S, E> = outcome::Outcome<S, E, E>;
/// User` and `Admin<'a>`) as the data is now owned by the request's cache.
///
/// [request-local state]: https://rocket.rs/master/guide/state/#request-local-state
// TODO: Typed: docs
#[crate::async_trait]
pub trait FromRequest<'r>: Sized {
/// The associated error to be returned if derivation fails.
Expand Down
10 changes: 9 additions & 1 deletion core/lib/src/route/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ pub type BoxFuture<'r, T = Outcome<'r>> = futures::future::BoxFuture<'r, T>;
/// This is an _async_ trait. Implementations must be decorated
/// [`#[rocket::async_trait]`](crate::async_trait).
///
/// ## Errors
///
/// If the handler errors or forwards, the implementation must include a
/// [`Box<dyn TypedError>`]. Any type that implements [`TypedError`] can
/// be boxed upcast, see [`TypedError`] docs for more information.
///
/// [`Box<dyn TypedError>`]: crate::catcher::TypedError
/// [`TypedError`]: crate::catcher::TypedError
///
/// # Example
///
/// Say you'd like to write a handler that changes its functionality based on an
Expand Down Expand Up @@ -137,7 +146,6 @@ pub type BoxFuture<'r, T = Outcome<'r>> = futures::future::BoxFuture<'r, T>;
/// Use this alternative when a single configuration is desired and your custom
/// handler is private to your application. For all other cases, a custom
/// `Handler` implementation is preferred.
// TODO: Typed: Docs
#[crate::async_trait]
pub trait Handler: Cloneable + Send + Sync + 'static {
/// Called by Rocket when a `Request` with its associated `Data` should be
Expand Down
2 changes: 0 additions & 2 deletions examples/error-handling/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,6 @@ fn test_hello_invalid_age() {
fn test_hello_sergio() {
let client = Client::tracked(super::rocket()).unwrap();

// TODO: typed: This logic has changed, either needs to be fixed
// or this test changed.
for path in &["oops", "-129"] {
let request = client.get(format!("/hello/Sergio/{}", path));
let expected = super::sergio_error();
Expand Down
2 changes: 0 additions & 2 deletions examples/serialization/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@ fn json_bad_get_put() {

// Try to put a message without a proper body.
let res = client.put("/json/80").header(ContentType::JSON).dispatch();
// TODO: Typed: This behavior has changed
assert_eq!(res.status(), Status::UnprocessableEntity);
// Status::BadRequest);

// Try to put a message with a semantically invalid body.
let res = client.put("/json/0")
Expand Down
2 changes: 1 addition & 1 deletion scripts/mk-docs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pushd "${PROJECT_ROOT}" > /dev/null 2>&1
--crate-version ${DOC_VERSION} \
--enable-index-page \
--generate-link-to-definition" \
cargo doc -Zrustdoc-map --no-deps --all-features \
cargo +nightly doc -Zrustdoc-map --no-deps --all-features \
-p rocket \
-p rocket_db_pools \
-p rocket_sync_db_pools \
Expand Down

0 comments on commit 0b72867

Please sign in to comment.