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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
use alux_ext::{ApplyAlg, ext};
/// Describes the capability to build typed handler endpoints.
pub trait HandlerAlg {
/// The interpreter's homogeneous endpoint representation.
type Endpoint;
}
/// Compiles a typed handler declaration supported by an interpreter.
pub trait HandlerEndpointAlg<Context, Inputs, Args, Transform, Output> {
/// Interprets a typed first-order handler as a concrete endpoint.
///
/// `Inputs`, `Args`, `Transform`, and `Output` remain type-level evidence;
/// only the handler value needs to be supplied at runtime.
fn finish_handler<Handler>(&self, handler: Handler) -> Self::Endpoint
where
Self: HandlerAlg,
Handler: ApplyAlg<Context, Args, Output = Output> + Send + Sync + 'static;
}
/// Interprets a named, defunctionalized HTTP program with `Compiler`.
pub trait HttpProgramAlg<Compiler> {
/// The route representation produced by `Compiler`.
type Route;
/// Compiles the program through the supplied interpreter.
fn compile_http(self, compiler: &Compiler) -> Self::Route;
}
/// Describes categorical composition of route selectors.
pub trait SelectorAlg {
/// The interpreter's selector representation.
type Selector;
/// Returns the selector identity.
fn identity(&self) -> Self::Selector;
/// Composes two selectors from left to right.
fn compose(&self, first: Self::Selector, second: Self::Selector) -> Self::Selector;
}
/// Describes categorical construction and composition of routes.
pub trait RouteAlg {
/// The interpreter's composed route representation.
type Route;
/// The selector applied to a route or endpoint.
type Selector;
/// The homogeneous endpoint representation lifted into routes.
type Endpoint;
/// Returns the initial route.
fn initial(&self) -> Self::Route;
/// Forms the coproduct of two routes.
fn coproduct(&self, left: Self::Route, right: Self::Route) -> Self::Route;
/// Precomposes a route with a selector.
fn precompose(&self, selector: Self::Selector, route: Self::Route) -> Self::Route;
/// Lifts an endpoint into a route.
fn lift(&self, endpoint: Self::Endpoint) -> Self::Route;
}
/// Describes the HTTP input roles chosen by an interpreter.
pub trait HttpInputAlg {
/// The interpreter's path extractor for `Input`.
type Path<Input>;
/// The interpreter's query extractor for `Input`.
type Query<Input>;
/// The interpreter's request-body extractor for `Input`.
type Body<Input>;
/// The interpreter's header extractor for `Input`.
type Header<Input>;
/// The interpreter's authentication extractor for `Input`.
type Auth<Input>;
/// The interpreter's endpoint-context extractor for `Input`.
type Context<Input>;
}
/// Describes HTTP selectors independently of route composition.
pub trait HttpSelectorAlg {
/// The interpreter's HTTP selector representation.
type Selector;
/// Interprets the GET method selector.
fn http_get(&self) -> Self::Selector;
/// Interprets the POST method selector.
fn http_post(&self) -> Self::Selector;
/// Interprets an exact path selector.
fn http_path(&self, path: &str) -> Self::Selector;
/// Interprets a path-prefix selector.
fn http_prefix(&self, prefix: &str) -> Self::Selector;
}
/// Combines the capabilities required to interpret HTTP route composition.
///
/// The alias states that HTTP selectors, route composition, and selector composition are witnessed
/// by one interpreter using a single selector representation.
pub trait HttpRouteAlg:
SelectorAlg
+ HttpSelectorAlg<Selector = <Self as SelectorAlg>::Selector>
+ RouteAlg<Selector = <Self as SelectorAlg>::Selector>
{
}
impl<This> HttpRouteAlg for This where
This: SelectorAlg
+ HttpSelectorAlg<Selector = <This as SelectorAlg>::Selector>
+ RouteAlg<Selector = <This as SelectorAlg>::Selector>
{
}
/// Combines the capabilities required to interpret a typed HTTP API.
///
/// The alias states that endpoints built by the handler capability are the endpoints lifted into
/// composed routes.
pub trait HttpApiAlg:
HandlerAlg + HttpInputAlg + HttpRouteAlg + RouteAlg<Endpoint = <Self as HandlerAlg>::Endpoint>
{
}
impl<This> HttpApiAlg for This where
This: HandlerAlg + HttpInputAlg + HttpRouteAlg + RouteAlg<Endpoint = <This as HandlerAlg>::Endpoint>
{
}
/// Carries a fluent route composition over an interpreter.
pub struct Routes<'a, Alg>
where
Alg: RouteAlg,
{
alg: &'a Alg,
route: Alg::Route,
}
impl<Alg> Routes<'_, Alg>
where
Alg: RouteAlg,
{
/// Returns the interpreted route value.
pub fn into_route(self) -> Alg::Route {
self.route
}
/// Forms a coproduct with another route composition.
#[must_use]
pub fn coproduct(self, other: Self) -> Self {
let route = self.alg.coproduct(self.route, other.route);
Self { alg: self.alg, route }
}
/// Merges another route composition into this one.
#[must_use]
pub fn merge(self, other: Self) -> Self {
self.coproduct(other)
}
}
impl<Alg> Routes<'_, Alg>
where
Alg: HttpRouteAlg,
{
fn append(self, route: Alg::Route) -> Self {
let route = self.alg.coproduct(self.route, route);
Self { alg: self.alg, route }
}
/// Adds an endpoint at an exact path.
#[must_use]
pub fn at(self, path: &str, endpoint: Alg::Endpoint) -> Self {
let route = self.alg.precompose(self.alg.http_path(path), self.alg.lift(endpoint));
self.append(route)
}
/// Nests another route composition under a path prefix.
#[must_use]
pub fn nest(self, prefix: &str, nested: Self) -> Self {
let route = self.alg.precompose(self.alg.http_prefix(prefix), nested.route);
self.append(route)
}
}
/// Provides fluent route composition on any `RouteAlg`.
#[ext(name = RouteAlgExt, supertraits = RouteAlg + Sized)]
pub impl<This> This
where
This: RouteAlg,
{
/// Starts with the initial route.
fn routes(&self) -> Routes<'_, Self> {
Routes { alg: self, route: self.initial() }
}
/// Wraps an already interpreted route for further fluent composition.
fn route(&self, route: This::Route) -> Routes<'_, Self> {
Routes { alg: self, route }
}
}
/// Compiles defunctionalized HTTP programs with an interpreter.
#[ext(name = HttpProgramExt)]
pub impl<This> This {
/// Compiles a named HTTP program with this interpreter.
fn compile_http<Program>(&self, program: Program) -> Program::Route
where
Program: HttpProgramAlg<This>,
{
program.compile_http(self)
}
}