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}