1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use crate::blueprint::conversions::raw_identifiers2callable;
use crate::blueprint::reflection::RawIdentifiers;
use crate::blueprint::router::MethodGuard;
use crate::blueprint::Blueprint;
use pavex_bp_schema::{Blueprint as BlueprintSchema, Callable, Component};

/// The type returned by [`Blueprint::route`].
///
/// It allows you to further configure the behaviour of the registered route.
///
/// [`Blueprint::route`]: Blueprint::route
pub struct RegisteredRoute<'a> {
    pub(crate) blueprint: &'a mut BlueprintSchema,
    /// The index of the registered route in the blueprint's `components` vector.
    pub(crate) component_id: usize,
}

impl<'a> RegisteredRoute<'a> {
    #[track_caller]
    /// Register an error handler.
    ///
    /// If an error handler has already been registered for this route, it will be
    /// overwritten.
    ///
    /// # Guide
    ///
    /// Check out the ["Error handlers"](https://pavex.dev/docs/guide/errors/error_handlers)
    /// section of Pavex's guide for a thorough introduction to error handlers
    /// in Pavex applications.
    ///
    /// # Example
    ///
    /// ```rust
    /// use pavex::f;
    /// use pavex::blueprint::{Blueprint, router::GET};
    /// use pavex::response::Response;
    /// # struct LogLevel;
    /// # struct RuntimeError;
    /// # struct ConfigurationError;
    ///
    /// // 👇 a fallible request handler
    /// fn request_handler() -> Result<Response, RuntimeError> {
    ///     // [...]
    ///     # todo!()
    /// }
    ///
    /// fn error_to_response(error: &ConfigurationError, log_level: LogLevel) -> Response {
    ///     // [...]
    ///     # todo!()
    /// }
    ///
    /// # fn main() {
    /// let mut bp = Blueprint::new();
    /// bp.route(GET, "/home", f!(crate::request_handler))
    ///     .error_handler(f!(crate::error_to_response));
    /// # }
    /// ```
    pub fn error_handler(mut self, error_handler: RawIdentifiers) -> Self {
        let callable = raw_identifiers2callable(error_handler);
        self.route().error_handler = Some(callable);
        self
    }

    fn route(&mut self) -> &mut pavex_bp_schema::Route {
        let component = &mut self.blueprint.components[self.component_id];
        let Component::Route(c) = component else {
            unreachable!("The component should be a route")
        };
        c
    }
}

/// A route that has been configured but has not yet been registered with a [`Blueprint`].
///
/// # Guide
///
/// Check out the ["Routing"](https://pavex.dev/docs/guide/routing) section of Pavex's guide
/// for a thorough introduction to routing in Pavex applications.
///
/// # Use cases
///
/// [`Route`] is primarily used by
/// [kits](https://pavex.dev/docs/guide/dependency_injection/kits)
/// to allow users to customize (or disable!)
/// the bundled routes **before** registering them with a [`Blueprint`].
#[derive(Clone, Debug)]
pub struct Route {
    pub(in crate::blueprint) method_guard: MethodGuard,
    pub(in crate::blueprint) path: String,
    pub(in crate::blueprint) callable: Callable,
    pub(in crate::blueprint) error_handler: Option<Callable>,
}

impl Route {
    /// Create a new (unregistered) route.
    ///
    /// Check out the documentation of [`Blueprint::route`] for more details
    /// on routes.
    #[track_caller]
    pub fn new(method_guard: MethodGuard, path: &str, callable: RawIdentifiers) -> Self {
        Self {
            callable: raw_identifiers2callable(callable),
            error_handler: None,
            method_guard,
            path: path.to_owned(),
        }
    }

    /// Register an error handler for this route.
    ///
    /// Check out the documentation of [`RegisteredRoute::error_handler`] for more details.
    #[track_caller]
    pub fn error_handler(mut self, error_handler: RawIdentifiers) -> Self {
        self.error_handler = Some(raw_identifiers2callable(error_handler));
        self
    }

    /// Register this route with a [`Blueprint`].
    ///
    /// Check out the documentation of [`Blueprint::route`] for more details.
    pub fn register(self, bp: &mut Blueprint) -> RegisteredRoute {
        bp.register_route(self)
    }
}