1use crate::{HttpMethod, HttpMethodAlg, HttpOperationAlg, Operation, RoutePath};
2use alux_ext::{ApplyAlg, OperationAlg, ext};
3use trait_set::trait_set;
4
5pub trait HandlerAlg {
7 type Endpoint;
9}
10
11pub trait HandlerEndpointAlg<Context, Inputs, Args, Transform, Output> {
13 fn finish_handler<Handler>(&self, handler: Handler) -> Self::Endpoint
21 where
22 Self: HandlerAlg,
23 Handler: OperationAlg + ApplyAlg<Context, Args, Output = Output> + Send + Sync + 'static;
24}
25
26pub trait HttpProgramAlg<Compiler> {
28 type Route;
30
31 fn compile_http(self, compiler: &Compiler) -> Self::Route;
33}
34
35pub trait SelectorAlg {
37 type Selector;
39
40 fn identity(&self) -> Self::Selector;
42
43 fn compose(&self, first: Self::Selector, second: Self::Selector) -> Self::Selector;
45}
46
47pub trait RouteAlg {
49 type Route;
51 type Selector;
53 type Endpoint;
55
56 fn initial(&self) -> Self::Route;
58
59 fn coproduct(&self, left: Self::Route, right: Self::Route) -> Self::Route;
61
62 fn precompose(&self, selector: Self::Selector, route: Self::Route) -> Self::Route;
64
65 fn lift(&self, endpoint: Self::Endpoint) -> Self::Route;
67}
68
69pub trait HttpInputAlg {
71 type Path<Input>;
73 type Query<Input>;
75 type Body<Input>;
77 type Form<Input>;
79 type RawBody<Input>;
81 type Header<Input>;
86 type Cookie<Input>;
88 type Multipart<Input>;
90 type Auth<Input>;
92 type Context<Input>;
94}
95
96pub trait HttpSelectorAlg {
98 type Selector;
100
101 fn http_method(&self, method: HttpMethod) -> Self::Selector;
103
104 fn http_path(&self, path: &RoutePath) -> Self::Selector;
106
107 fn http_prefix(&self, prefix: &RoutePath) -> Self::Selector;
109}
110
111trait_set! {
112 pub trait HttpRouteAlg = SelectorAlg
117 + HttpSelectorAlg<Selector = <Self as SelectorAlg>::Selector>
118 + RouteAlg<Selector = <Self as SelectorAlg>::Selector>;
119
120 pub trait HttpApiAlg =
125 HandlerAlg + HttpInputAlg + HttpRouteAlg + RouteAlg<Endpoint = <Self as HandlerAlg>::Endpoint>;
126}
127
128pub struct Routes<'a, Alg>
130where
131 Alg: RouteAlg,
132{
133 alg: &'a Alg,
134 route: Alg::Route,
135}
136
137impl<Alg> Routes<'_, Alg>
138where
139 Alg: RouteAlg,
140{
141 pub fn into_route(self) -> Alg::Route {
143 self.route
144 }
145
146 #[must_use]
148 pub fn coproduct(self, other: Self) -> Self {
149 let route = self.alg.coproduct(self.route, other.route);
150 Self { alg: self.alg, route }
151 }
152
153 #[must_use]
155 pub fn merge(self, other: Self) -> Self {
156 self.coproduct(other)
157 }
158}
159
160impl<Alg> Routes<'_, Alg>
161where
162 Alg: HttpRouteAlg,
163{
164 fn append(self, route: Alg::Route) -> Self {
165 let route = self.alg.coproduct(self.route, route);
166 Self { alg: self.alg, route }
167 }
168
169 #[must_use]
171 pub fn method<Method, Handler, Inputs, Args, Kind>(
172 self,
173 path: &str,
174 operation: Operation<Handler, Inputs, Args, Kind>,
175 ) -> Self
176 where
177 Method: HttpMethodAlg,
178 Alg: HttpOperationAlg<Handler, Inputs, Kind, Endpoint = <Alg as RouteAlg>::Endpoint>,
179 {
180 let selector =
181 self.alg.compose(self.alg.http_method(Method::METHOD), self.alg.http_path(&RoutePath::parse(path)));
182 let endpoint = self.alg.http_operation(operation.handler);
183 let route = self.alg.precompose(selector, self.alg.lift(endpoint));
184 self.append(route)
185 }
186
187 #[must_use]
189 pub fn at(self, path: &str, endpoint: Alg::Endpoint) -> Self {
190 let route = self.alg.precompose(self.alg.http_path(&RoutePath::parse(path)), self.alg.lift(endpoint));
191 self.append(route)
192 }
193
194 #[must_use]
196 pub fn nest(self, prefix: &str, nested: Self) -> Self {
197 let route = self.alg.precompose(self.alg.http_prefix(&RoutePath::parse(prefix)), nested.route);
198 self.append(route)
199 }
200}
201
202#[ext(name = RouteAlgExt)]
204pub impl<This> This
205where
206 This: RouteAlg,
207{
208 fn routes(&self) -> Routes<'_, This> {
210 Routes { alg: self, route: self.initial() }
211 }
212
213 fn route(&self, route: This::Route) -> Routes<'_, This> {
215 Routes { alg: self, route }
216 }
217}
218
219#[ext(name = HttpProgramExt)]
221pub impl<This> This {
222 fn program<Program>(&self, program: Program) -> Routes<'_, This>
224 where
225 This: RouteAlg,
226 Program: HttpProgramAlg<This, Route = This::Route>,
227 {
228 self.route(program.compile_http(self))
229 }
230
231 fn compile_http<Program>(&self, program: Program) -> Program::Route
233 where
234 Program: HttpProgramAlg<This>,
235 {
236 program.compile_http(self)
237 }
238}
239
240#[ext(name = HttpOperationExt)]
242pub impl<This> This {
243 fn op<Handler>(&self, handler: Handler) -> Operation<Handler> {
245 Operation::new(handler)
246 }
247}
248
249macro_rules! interpreted_routes {
250 ($($method:ident => $marker:ident, $label:literal),+ $(,)?) => {
251 impl<Alg> Routes<'_, Alg>
252 where
253 Alg: HttpRouteAlg,
254 {
255 $(
256 #[doc = concat!("Interprets an endpoint selected by `", $label, "` and an exact path.")]
257 #[must_use]
258 pub fn $method<Handler, Inputs, Args, Kind>(
259 self,
260 path: &str,
261 operation: Operation<Handler, Inputs, Args, Kind>,
262 ) -> Self
263 where
264 Alg: HttpOperationAlg<Handler, Inputs, Kind, Endpoint = <Alg as RouteAlg>::Endpoint>,
265 {
266 self.method::<crate::$marker, _, _, _, _>(path, operation)
267 }
268 )+
269 }
270 };
271}
272
273with_http_methods!(interpreted_routes);