pavex/blueprint/fallback.rs
1use crate::blueprint::ErrorHandler;
2use crate::blueprint::conversions::coordinates2coordinates;
3use pavex_bp_schema::{Blueprint as BlueprintSchema, Component, Location};
4
5use super::reflection::AnnotationCoordinates;
6
7/// The input type for [`Blueprint::fallback`].
8///
9/// Check out [`Blueprint::fallback`] for more information on fallback routes
10/// in Pavex.
11///
12/// # Stability guarantees
13///
14/// Use the [`fallback`](macro@crate::fallback) attribute macro to create instances of `Fallback`.\
15/// `Fallback`'s fields are an implementation detail of Pavex's macros and should not be relied upon:
16/// newer versions of Pavex may add, remove or modify its fields.
17///
18/// [`Blueprint::fallback`]: crate::Blueprint::fallback
19pub struct Fallback {
20 #[doc(hidden)]
21 pub coordinates: AnnotationCoordinates,
22}
23
24/// The type returned by [`Blueprint::fallback`].
25///
26/// It allows you to further configure the behaviour of the registered handler.
27///
28/// [`Blueprint::fallback`]: crate::Blueprint::fallback
29pub struct RegisteredFallback<'a> {
30 pub(crate) blueprint: &'a mut BlueprintSchema,
31 /// The index of the registered fallback in the blueprint's `components` vector.
32 pub(crate) component_id: usize,
33}
34
35impl RegisteredFallback<'_> {
36 #[track_caller]
37 /// Register an error handler.
38 ///
39 /// Error handlers convert the error type returned by your request handler into an HTTP response.
40 ///
41 /// Error handlers CANNOT consume the error type, they must take a reference to the
42 /// error as input.
43 /// Error handlers can have additional input parameters alongside the error, as long as there
44 /// are constructors registered for those parameter types.
45 ///
46 /// ```rust
47 /// use pavex::Blueprint;
48 /// use pavex::{error_handler, fallback};
49 /// use pavex::Response;
50 /// # struct LogLevel;
51 /// # struct RuntimeError;
52 ///
53 /// // 👇 a fallible fallback handler
54 /// #[fallback]
55 /// pub fn fallback_handler() -> Result<Response, RuntimeError> {
56 /// // [...]
57 /// # todo!()
58 /// }
59 ///
60 /// #[error_handler]
61 /// pub fn runtime_error_handler(
62 /// #[px(error_ref)] error: &RuntimeError,
63 /// log_level: LogLevel
64 /// ) -> Response {
65 /// // [...]
66 /// # todo!()
67 /// }
68 ///
69 /// # fn main() {
70 /// let mut bp = Blueprint::new();
71 /// bp.fallback(FALLBACK_HANDLER)
72 /// .error_handler(RUNTIME_ERROR_HANDLER);
73 /// # }
74 /// ```
75 ///
76 /// If an error handler has already been registered for the same error type, it will be
77 /// overwritten.
78 ///
79 /// ## Common Errors
80 ///
81 /// Pavex will fail to generate the runtime code for your application if you register
82 /// an error handler for an infallible request handler (i.e. a request handler that doesn't
83 /// return a `Result`).
84 pub fn error_handler(mut self, error_handler: ErrorHandler) -> Self {
85 let error_handler = pavex_bp_schema::ErrorHandler {
86 coordinates: coordinates2coordinates(error_handler.coordinates),
87 registered_at: Location::caller(),
88 };
89 self.fallback().error_handler = Some(error_handler);
90 self
91 }
92
93 fn fallback(&mut self) -> &mut pavex_bp_schema::Fallback {
94 let component = &mut self.blueprint.components[self.component_id];
95 let Component::FallbackRequestHandler(fallback) = component else {
96 unreachable!("The component should be a fallback request handler")
97 };
98 fallback
99 }
100}