Skip to content

Commit 4a00c1f

Browse files
committed
Improve 'Error' type: make 'ErrorKind' accessible.
This commit improves the 'Error' type such that: - It is now fully documented. - The `ErrorKind` enum variant fields are all publicly reachable. - The `Sentry` type is exposed. This is a breaking change: - `ErrorKind::Collisions` is now struct-like with two fields.
1 parent 926e06e commit 4a00c1f

File tree

7 files changed

+135
-39
lines changed

7 files changed

+135
-39
lines changed

core/codegen/tests/route-ranking.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ fn test_rank_collision() {
4646
let rocket = rocket::build().mount("/", routes![get0, get0b]);
4747
let client_result = Client::debug(rocket);
4848
match client_result.as_ref().map_err(|e| e.kind()) {
49-
Err(ErrorKind::Collisions(..)) => { /* o.k. */ },
49+
Err(ErrorKind::Collisions { .. }) => { /* o.k. */ },
5050
Ok(_) => panic!("client succeeded unexpectedly"),
5151
Err(e) => panic!("expected collision, got {}", e)
5252
}

core/lib/src/error.rs

+77-20
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,45 @@ use std::sync::Arc;
77
use figment::Profile;
88

99
use crate::listener::Endpoint;
10-
use crate::{Ignite, Orbit, Phase, Rocket};
10+
use crate::{Catcher, Ignite, Orbit, Phase, Rocket, Route};
1111
use crate::trace::Trace;
1212

13-
/// An error that occurs during launch.
13+
/// An error that occurred during launch or ignition.
1414
///
15-
/// An `Error` is returned by [`launch()`](Rocket::launch()) when launching an
16-
/// application fails or, more rarely, when the runtime fails after launching.
15+
/// An `Error` is returned by [`Rocket::launch()`] or [`Rocket::ignite()`] on
16+
/// failure to launch or ignite, respectively. An `Error` may occur when the
17+
/// configuration is invalid, when a route or catcher collision is detected, or
18+
/// when a fairing fails to launch. An `Error` may also occur when the Rocket
19+
/// instance fails to liftoff or when the Rocket instance fails to shutdown.
20+
/// Finally, an `Error` may occur when a sentinel requests an abort.
1721
///
18-
/// # Usage
22+
/// To determine the kind of error that occurred, use [`Error::kind()`].
1923
///
20-
/// An `Error` value should usually be allowed to `drop` without inspection.
21-
/// There are at least two exceptions:
24+
/// # Example
2225
///
23-
/// 1. If you are writing a library or high-level application on-top of
24-
/// Rocket, you likely want to inspect the value before it drops to avoid a
25-
/// Rocket-specific `panic!`. This typically means simply printing the
26-
/// value.
26+
/// ```rust
27+
/// # use rocket::*;
28+
/// use rocket::trace::Trace;
29+
/// use rocket::error::ErrorKind;
2730
///
28-
/// 2. You want to display your own error messages.
31+
/// # async fn run() -> Result<(), rocket::error::Error> {
32+
/// if let Err(e) = rocket::build().ignite().await {
33+
/// match e.kind() {
34+
/// ErrorKind::Bind(_, e) => info!("binding failed: {}", e),
35+
/// ErrorKind::Io(e) => info!("I/O error: {}", e),
36+
/// _ => e.trace_error(),
37+
/// }
38+
///
39+
/// return Err(e);
40+
/// }
41+
/// # Ok(())
42+
/// # }
43+
/// ```
2944
pub struct Error {
3045
pub(crate) kind: ErrorKind
3146
}
3247

33-
/// The kind error that occurred.
48+
/// The error kind that occurred. Returned by [`Error::kind()`].
3449
///
3550
/// In almost every instance, a launch error occurs because of an I/O error;
3651
/// this is represented by the `Io` variant. A launch error may also occur
@@ -39,17 +54,22 @@ pub struct Error {
3954
/// `FailedFairing` variants, respectively.
4055
#[derive(Debug)]
4156
#[non_exhaustive]
42-
// FIXME: Don't expose this. Expose access methods from `Error` instead.
4357
pub enum ErrorKind {
44-
/// Binding to the network interface at `.0` failed with error `.1`.
58+
/// Binding to the network interface at `.0` (if known) failed with `.1`.
4559
Bind(Option<Endpoint>, Box<dyn StdError + Send>),
4660
/// An I/O error occurred during launch.
4761
Io(io::Error),
4862
/// A valid [`Config`](crate::Config) could not be extracted from the
4963
/// configured figment.
5064
Config(figment::Error),
51-
/// Route collisions were detected.
52-
Collisions(crate::router::Collisions),
65+
/// Route or catcher collisions were detected. At least one of `routes` or
66+
/// `catchers` is guaranteed to be non-empty.
67+
Collisions {
68+
/// Pairs of colliding routes, if any.
69+
routes: Vec<(Route, Route)>,
70+
/// Pairs of colliding catchers, if any.
71+
catchers: Vec<(Catcher, Catcher)>,
72+
},
5373
/// Launch fairing(s) failed.
5474
FailedFairings(Vec<crate::fairing::Info>),
5575
/// Sentinels requested abort.
@@ -75,11 +95,48 @@ impl Error {
7595
Error { kind }
7696
}
7797

78-
// FIXME: Don't expose this. Expose finer access methods instead.
98+
/// Returns the kind of error that occurred.
99+
///
100+
/// # Example
101+
///
102+
/// ```rust
103+
/// # use rocket::*;
104+
/// use rocket::trace::Trace;
105+
/// use rocket::error::ErrorKind;
106+
///
107+
/// # async fn run() -> Result<(), rocket::error::Error> {
108+
/// if let Err(e) = rocket::build().ignite().await {
109+
/// match e.kind() {
110+
/// ErrorKind::Bind(_, e) => info!("binding failed: {}", e),
111+
/// ErrorKind::Io(e) => info!("I/O error: {}", e),
112+
/// _ => e.trace_error(),
113+
/// }
114+
/// }
115+
/// # Ok(())
116+
/// # }
117+
/// ```
79118
pub fn kind(&self) -> &ErrorKind {
80119
&self.kind
81120
}
82121

122+
/// Given the return value of [`Rocket::launch()`] or [`Rocket::ignite()`],
123+
/// which return a `Result<Rocket<P>, Error>`, logs the error, if any, and
124+
/// returns the appropriate exit code.
125+
///
126+
/// For `Ok(_)`, returns `ExitCode::SUCCESS`. For `Err(e)`, logs the error
127+
/// and returns `ExitCode::FAILURE`.
128+
///
129+
/// # Example
130+
///
131+
/// ```rust
132+
/// # use rocket::*;
133+
/// use std::process::ExitCode;
134+
/// use rocket::error::Error;
135+
///
136+
/// async fn run() -> ExitCode {
137+
/// Error::report(rocket::build().launch().await)
138+
/// }
139+
/// ```
83140
pub fn report<P: Phase>(result: Result<Rocket<P>, Error>) -> process::ExitCode {
84141
match result {
85142
Ok(_) => process::ExitCode::SUCCESS,
@@ -114,7 +171,7 @@ impl StdError for Error {
114171
match &self.kind {
115172
ErrorKind::Bind(_, e) => Some(&**e),
116173
ErrorKind::Io(e) => Some(e),
117-
ErrorKind::Collisions(_) => None,
174+
ErrorKind::Collisions { .. } => None,
118175
ErrorKind::FailedFairings(_) => None,
119176
ErrorKind::InsecureSecretKey(_) => None,
120177
ErrorKind::Config(e) => Some(e),
@@ -131,7 +188,7 @@ impl fmt::Display for ErrorKind {
131188
match self {
132189
ErrorKind::Bind(_, e) => write!(f, "binding failed: {e}"),
133190
ErrorKind::Io(e) => write!(f, "I/O error: {e}"),
134-
ErrorKind::Collisions(_) => "collisions detected".fmt(f),
191+
ErrorKind::Collisions { .. } => "collisions detected".fmt(f),
135192
ErrorKind::FailedFairings(_) => "launch fairing(s) failed".fmt(f),
136193
ErrorKind::InsecureSecretKey(_) => "insecure secret key config".fmt(f),
137194
ErrorKind::Config(_) => "failed to extract configuration".fmt(f),

core/lib/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ mod erased;
174174
#[doc(inline)] pub use crate::route::Route;
175175
#[doc(inline)] pub use crate::phase::{Phase, Build, Ignite, Orbit};
176176
#[doc(inline)] pub use crate::error::Error;
177-
#[doc(inline)] pub use crate::sentinel::Sentinel;
177+
#[doc(inline)] pub use crate::sentinel::{Sentinel, Sentry};
178178
#[doc(inline)] pub use crate::request::Request;
179179
#[doc(inline)] pub use crate::rkt::Rocket;
180180
#[doc(inline)] pub use crate::shutdown::Shutdown;

core/lib/src/rocket.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,7 @@ impl Rocket<Build> {
556556
let mut router = Router::new();
557557
self.routes.clone().into_iter().for_each(|r| router.add_route(r));
558558
self.catchers.clone().into_iter().for_each(|c| router.add_catcher(c));
559-
router.finalize().map_err(ErrorKind::Collisions)?;
559+
router.finalize().map_err(|(r, c)| ErrorKind::Collisions { routes: r, catchers: c, })?;
560560

561561
// Finally, freeze managed state for faster access later.
562562
self.state.freeze();

core/lib/src/router/router.rs

+3-7
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,7 @@ pub(crate) struct Router {
1212
catchers: HashMap<Option<u16>, Vec<Catcher>>,
1313
}
1414

15-
#[derive(Debug)]
16-
pub struct Collisions {
17-
pub routes: Vec<(Route, Route)>,
18-
pub catchers: Vec<(Catcher, Catcher)>,
19-
}
15+
pub type Collisions<T> = Vec<(T, T)>;
2016

2117
impl Router {
2218
pub fn new() -> Self {
@@ -84,12 +80,12 @@ impl Router {
8480
})
8581
}
8682

87-
pub fn finalize(&self) -> Result<(), Collisions> {
83+
pub fn finalize(&self) -> Result<(), (Collisions<Route>, Collisions<Catcher>)> {
8884
let routes: Vec<_> = self.collisions(self.routes()).collect();
8985
let catchers: Vec<_> = self.collisions(self.catchers()).collect();
9086

9187
if !routes.is_empty() || !catchers.is_empty() {
92-
return Err(Collisions { routes, catchers })
88+
return Err((routes, catchers))
9389
}
9490

9591
Ok(())

core/lib/src/sentinel.rs

+49-1
Original file line numberDiff line numberDiff line change
@@ -317,25 +317,73 @@ impl<T> Sentinel for crate::response::Debug<T> {
317317
}
318318
}
319319

320-
/// The information resolved from a `T: ?Sentinel` by the `resolve!()` macro.
320+
/// Information resolved at compile-time from eligible [`Sentinel`] types.
321+
///
322+
/// Returned as a result of the [`ignition`](Rocket::ignite()) method, this
323+
/// struct contains information about a resolved sentinel including the type ID
324+
/// and type name. It is made available via the [`ErrorKind::SentinelAborts`]
325+
/// variant of the [`ErrorKind`] enum.
326+
///
327+
/// [`ErrorKind`]: crate::error::ErrorKind
328+
/// [`ErrorKind::SentinelAborts`]: crate::error::ErrorKind::SentinelAborts
329+
///
330+
// The information resolved from a `T: ?Sentinel` by the `resolve!()` macro.
321331
#[derive(Clone, Copy)]
322332
pub struct Sentry {
323333
/// The type ID of `T`.
334+
#[doc(hidden)]
324335
pub type_id: TypeId,
325336
/// The type name `T` as a string.
337+
#[doc(hidden)]
326338
pub type_name: &'static str,
327339
/// The type ID of type in which `T` is nested if not a top-level type.
340+
#[doc(hidden)]
328341
pub parent: Option<TypeId>,
329342
/// The source (file, column, line) location of the resolved `T`.
343+
#[doc(hidden)]
330344
pub location: (&'static str, u32, u32),
331345
/// The value of `<T as Sentinel>::SPECIALIZED` or the fallback.
332346
///
333347
/// This is `true` when `T: Sentinel` and `false` when `T: !Sentinel`.
348+
#[doc(hidden)]
334349
pub specialized: bool,
335350
/// The value of `<T as Sentinel>::abort` or the fallback.
351+
#[doc(hidden)]
336352
pub abort: fn(&Rocket<Ignite>) -> bool,
337353
}
338354

355+
impl Sentry {
356+
/// Returns the type ID of the resolved sentinal type.
357+
///
358+
/// # Example
359+
///
360+
/// ```rust
361+
/// use rocket::Sentry;
362+
///
363+
/// fn handle_error(sentry: &Sentry) {
364+
/// let type_id = sentry.type_id();
365+
/// }
366+
/// ```
367+
pub fn type_id(&self) -> TypeId {
368+
self.type_id
369+
}
370+
371+
/// Returns the type name of the resolved sentinal type.
372+
///
373+
/// # Example
374+
///
375+
/// ```rust
376+
/// use rocket::Sentry;
377+
///
378+
/// fn handle_error(sentry: &Sentry) {
379+
/// let type_name = sentry.type_name();
380+
/// println!("Type name: {}", type_name);
381+
/// }
382+
pub fn type_name(&self) -> &'static str {
383+
self.type_name
384+
}
385+
}
386+
339387
/// Query `sentinels`, once for each unique `type_id`, returning an `Err` of all
340388
/// of the sentinels that triggered an abort or `Ok(())` if none did.
341389
pub(crate) fn query<'s>(

core/lib/src/trace/traceable.rs

+3-8
Original file line numberDiff line numberDiff line change
@@ -314,24 +314,19 @@ impl Trace for ErrorKind {
314314
}
315315
Io(reason) => event!(level, "error::io", %reason, "i/o error"),
316316
Config(error) => error.trace(level),
317-
Collisions(collisions) => {
318-
let routes = collisions.routes.len();
319-
let catchers = collisions.catchers.len();
320-
317+
Collisions { routes, catchers }=> {
321318
span!(level, "collision",
322-
route.pairs = routes,
323-
catcher.pairs = catchers,
319+
route.pairs = routes.len(),
320+
catcher.pairs = catchers.len(),
324321
"colliding items detected"
325322
).in_scope(|| {
326-
let routes = &collisions.routes;
327323
for (a, b) in routes {
328324
span!(level, "colliding route pair").in_scope(|| {
329325
a.trace(level);
330326
b.trace(level);
331327
})
332328
}
333329

334-
let catchers = &collisions.catchers;
335330
for (a, b) in catchers {
336331
span!(level, "colliding catcher pair").in_scope(|| {
337332
a.trace(level);

0 commit comments

Comments
 (0)