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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
//! The schema used by Pavex to serialize and deserialize blueprints.
//!
//! There are no guarantees that this schema will remain stable across Pavex versions:
//! it is considered (for the time being) an internal implementation detail of Pavex's reflection system.
pub use pavex_reflection::{Location, RawCallableIdentifiers};
use std::collections::{BTreeMap, BTreeSet};
use std::fmt;
use std::fmt::Formatter;

#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
pub struct Blueprint {
    /// The location where the `Blueprint` was created.
    pub creation_location: Location,
    /// All registered components, in the order they were registered.
    pub components: Vec<Component>,
}

#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
pub enum Component {
    Constructor(Constructor),
    WrappingMiddleware(WrappingMiddleware),
    Route(Route),
    FallbackRequestHandler(Fallback),
    NestedBlueprint(NestedBlueprint),
    ErrorObserver(ErrorObserver),
}

impl From<Constructor> for Component {
    fn from(c: Constructor) -> Self {
        Self::Constructor(c)
    }
}

impl From<WrappingMiddleware> for Component {
    fn from(m: WrappingMiddleware) -> Self {
        Self::WrappingMiddleware(m)
    }
}

impl From<Route> for Component {
    fn from(r: Route) -> Self {
        Self::Route(r)
    }
}

impl From<Fallback> for Component {
    fn from(f: Fallback) -> Self {
        Self::FallbackRequestHandler(f)
    }
}

impl From<NestedBlueprint> for Component {
    fn from(b: NestedBlueprint) -> Self {
        Self::NestedBlueprint(b)
    }
}

impl From<ErrorObserver> for Component {
    fn from(e: ErrorObserver) -> Self {
        Self::ErrorObserver(e)
    }
}

#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
/// A route registered against a `Blueprint` via `Blueprint::route`.
pub struct Route {
    /// The path of the route.
    pub path: String,
    /// The HTTP method guard for the route.
    pub method_guard: MethodGuard,
    /// The callable in charge of processing incoming requests for this route.
    pub request_handler: Callable,
    /// The callable in charge of processing errors returned by the request handler, if any.
    pub error_handler: Option<Callable>,
}

#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
/// A request handler registered against a `Blueprint` via `Blueprint::fallback` to
/// process requests that don't match any of the registered routes.
pub struct Fallback {
    /// The callable in charge of processing incoming requests.
    pub request_handler: Callable,
    /// The callable in charge of processing errors returned by the request handler, if any.
    pub error_handler: Option<Callable>,
}

#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
/// An error observer registered against a `Blueprint` via `Blueprint::error_observer` to
/// intercept unhandled errors.
pub struct ErrorObserver {
    /// The callable in charge of processing unhandled errors.
    pub error_observer: Callable,
}

#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
/// A constructor registered against a `Blueprint` via `Blueprint::constructor`.
pub struct Constructor {
    /// The callable in charge of constructing the desired type.
    pub constructor: Callable,
    /// The lifecycle of the constructed type.
    pub lifecycle: Lifecycle,
    /// The strategy dictating when the constructed type can be cloned.
    pub cloning_strategy: Option<CloningStrategy>,
    /// The callable in charge of processing errors returned by this constructor, if any.
    pub error_handler: Option<Callable>,
    /// Lint settings for this constructor.
    pub lints: BTreeMap<Lint, LintSetting>,
}

#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
/// A middleware registered against a `Blueprint` via `Blueprint::wrap`.
pub struct WrappingMiddleware {
    /// The callable that executes the middleware's logic.
    pub middleware: Callable,
    /// The callable in charge of processing errors returned by this middleware, if any.
    pub error_handler: Option<Callable>,
}

#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
/// A "callable" registered against a `Blueprint`—either a free function or a method,
/// used as a request handler, error handler or constructor.
pub struct Callable {
    /// Metadata that uniquely identifies the callable.
    pub callable: RawCallableIdentifiers,
    /// The location where the callable was registered against the `Blueprint`.
    pub location: Location,
}

#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
/// A `Blueprint` that has been nested inside another `Blueprint` via `Blueprint::nest` or
/// `Blueprint::nest_at`.
pub struct NestedBlueprint {
    /// The nested `Blueprint`.
    pub blueprint: Blueprint,
    /// The path prefix that will prepended to all routes registered against the nested
    /// `Blueprint`.
    /// If `None`, the routes coming from the nested `Blueprint` will be registered as-they-are.
    pub path_prefix: Option<String>,
    /// The location where the `Blueprint` was nested under its parent `Blueprint`.
    pub nesting_location: Location,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum Lifecycle {
    Singleton,
    RequestScoped,
    Transient,
}

impl fmt::Display for Lifecycle {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            Lifecycle::Singleton => write!(f, "singleton"),
            Lifecycle::RequestScoped => write!(f, "request-scoped"),
            Lifecycle::Transient => write!(f, "transient"),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
#[non_exhaustive]
pub enum CloningStrategy {
    /// Pavex will **never** try clone the output type returned by the constructor.
    NeverClone,
    /// Pavex will only clone the output type returned by this constructor if it's
    /// necessary to generate code that satisfies Rust's borrow checker.
    CloneIfNecessary,
}

#[derive(
    Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd, serde::Serialize, serde::Deserialize,
)]
pub enum MethodGuard {
    Any,
    Some(BTreeSet<String>),
}

#[derive(
    Debug, Clone, Copy, Eq, Ord, PartialOrd, PartialEq, Hash, serde::Serialize, serde::Deserialize,
)]
#[non_exhaustive]
/// Common mistakes and antipatterns that Pavex
/// tries to catch when analysing your [`Blueprint`].  
pub enum Lint {
    /// You registered a component that's never used in the generated
    /// server SDK code.
    Unused,
}

#[derive(
    Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize,
)]
pub enum LintSetting {
    Ignore,
    Enforce,
}