1use crate::{
2 BytesOut, Connect, Delete, EmptyOut, FileOut, Get, HandlerEndpointAlg, Head, HeaderOut, HtmlOut, HttpApiAlg,
3 HttpInputAlg, HttpMethodAlg, HttpProgramAlg, HttpRouteAlg, JsonOut, NamedValuesAlg, Options, Patch, Post, Put,
4 RedirectOut, ResultOut, RouteAlg, RoutePath, StatusOut, StreamOut, TextOut, Trace, WithAlg,
5};
6use alux_ext::{ApplyAlg, HandlerContextAlg, OperationAlg};
7use core::marker::PhantomData;
8
9pub trait CompileRouteProgram<Compiler> {
11 type Route;
13
14 fn compile_route(self, compiler: &Compiler) -> Self::Route;
19}
20
21#[derive(Debug, Default)]
23pub struct Empty;
24
25#[derive(Debug)]
27pub struct Merge<Left, Right> {
28 left: Left,
29 right: Right,
30}
31
32#[derive(Debug)]
34pub struct Nest<Program> {
35 prefix: RoutePath,
36 program: Program,
37}
38
39#[derive(Debug)]
41pub struct Named<Program>(Program);
42
43#[derive(Debug)]
45pub struct Endpoint<Method, Handler, Inputs, Args, Transform> {
46 path: RoutePath,
47 handler: Handler,
48 marker: PhantomData<fn(Method, Inputs, Args, Transform)>,
49}
50
51#[derive(Debug)]
53pub struct Operation<Handler, Inputs = (), Args = (), Transform = ()> {
54 handler: Handler,
55 marker: PhantomData<fn(Inputs, Args, Transform)>,
56}
57
58#[derive(Debug)]
60pub struct RouteProgram<Program>(Program);
61
62#[derive(Debug, Default)]
64pub struct HttpProgramBuilder;
65
66pub struct Direct<Input>(PhantomData<Input>);
68
69pub struct Path<Input>(PhantomData<Input>);
71
72pub struct Query<Input>(PhantomData<Input>);
74
75pub struct Body<Input>(PhantomData<Input>);
77
78pub struct Form<Input>(PhantomData<Input>);
80
81pub struct RawBody<Input>(PhantomData<Input>);
83
84pub struct Header<Input>(PhantomData<Input>);
86
87pub struct Cookie<Input>(PhantomData<Input>);
89
90pub struct Multipart<Input>(PhantomData<Input>);
92
93pub struct Auth<Input>(PhantomData<Input>);
95
96pub struct Context<Input>(PhantomData<Input>);
98
99pub trait InterpretInputsAlg<Compiler> {
101 type Inputs;
103}
104
105impl<Compiler> InterpretInputsAlg<Compiler> for () {
106 type Inputs = ();
107}
108
109impl<Compiler, Input> InterpretInputsAlg<Compiler> for Direct<Input> {
110 type Inputs = Input;
111}
112
113impl<Compiler, Input> InterpretInputsAlg<Compiler> for Path<Input>
114where
115 Compiler: HttpInputAlg,
116{
117 type Inputs = Compiler::Path<Input>;
118}
119
120impl<Compiler, Input> InterpretInputsAlg<Compiler> for Query<Input>
121where
122 Compiler: HttpInputAlg,
123{
124 type Inputs = Compiler::Query<Input>;
125}
126
127impl<Compiler, Input> InterpretInputsAlg<Compiler> for Body<Input>
128where
129 Compiler: HttpInputAlg,
130{
131 type Inputs = Compiler::Body<Input>;
132}
133
134impl<Compiler, Input> InterpretInputsAlg<Compiler> for Form<Input>
135where
136 Compiler: HttpInputAlg,
137{
138 type Inputs = Compiler::Form<Input>;
139}
140
141impl<Compiler, Input> InterpretInputsAlg<Compiler> for RawBody<Input>
142where
143 Compiler: HttpInputAlg,
144{
145 type Inputs = Compiler::RawBody<Input>;
146}
147
148impl<Compiler, Input> InterpretInputsAlg<Compiler> for Header<Input>
149where
150 Compiler: HttpInputAlg,
151{
152 type Inputs = Compiler::Header<Input>;
153}
154
155impl<Compiler, Input> InterpretInputsAlg<Compiler> for Cookie<Input>
156where
157 Compiler: HttpInputAlg,
158{
159 type Inputs = Compiler::Cookie<Input>;
160}
161
162impl<Compiler, Input> InterpretInputsAlg<Compiler> for Multipart<Input>
163where
164 Compiler: HttpInputAlg,
165{
166 type Inputs = Compiler::Multipart<Input>;
167}
168
169impl<Compiler, Input> InterpretInputsAlg<Compiler> for Auth<Input>
170where
171 Compiler: HttpInputAlg,
172{
173 type Inputs = Compiler::Auth<Input>;
174}
175
176impl<Compiler, Input> InterpretInputsAlg<Compiler> for Context<Input>
177where
178 Compiler: HttpInputAlg,
179{
180 type Inputs = Compiler::Context<Input>;
181}
182
183macro_rules! interpret_inputs {
184 ($($input:ident),+ $(,)?) => {
185 impl<Compiler, $($input),+> InterpretInputsAlg<Compiler> for ($($input,)+)
186 where
187 $($input: InterpretInputsAlg<Compiler>,)+
188 {
189 type Inputs = ($($input::Inputs,)+);
190 }
191 };
192}
193
194interpret_inputs!(I1);
195interpret_inputs!(I1, I2);
196interpret_inputs!(I1, I2, I3);
197interpret_inputs!(I1, I2, I3, I4);
198interpret_inputs!(I1, I2, I3, I4, I5);
199interpret_inputs!(I1, I2, I3, I4, I5, I6);
200interpret_inputs!(I1, I2, I3, I4, I5, I6, I7);
201interpret_inputs!(I1, I2, I3, I4, I5, I6, I7, I8);
202interpret_inputs!(I1, I2, I3, I4, I5, I6, I7, I8, I9);
203interpret_inputs!(I1, I2, I3, I4, I5, I6, I7, I8, I9, I10);
204interpret_inputs!(I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11);
205interpret_inputs!(I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12);
206interpret_inputs!(I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13);
207interpret_inputs!(I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14);
208interpret_inputs!(I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15);
209interpret_inputs!(I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15, I16);
210
211impl HttpProgramBuilder {
212 pub fn routes(&self) -> RouteProgram<Empty> {
217 RouteProgram(Empty)
218 }
219
220 pub fn op<Handler>(&self, handler: Handler) -> Operation<Handler> {
225 Operation { handler, marker: PhantomData }
226 }
227
228 pub fn program<Program>(&self, program: Program) -> RouteProgram<Named<Program>> {
233 RouteProgram(Named(program))
234 }
235}
236
237pub type WithInput<Handler, Inputs, Args, Transform, Extractor, Arg> =
239 Operation<Handler, <Inputs as WithAlg>::With<Extractor>, <Args as WithAlg>::With<Arg>, Transform>;
240
241pub type WithEndpoint<Program, Method, Handler, Inputs, Args, Transform> =
243 RouteProgram<Merge<Program, Endpoint<Method, Handler, Inputs, Args, Transform>>>;
244
245macro_rules! output_methods {
246 ($($method:ident => $kind:ident, $alg:ident, $selected:ident, $meaning:literal),+ $(,)?) => {
247 $(
248 #[doc = concat!("Marks the inferred handler result for ", $meaning, " interpretation.")]
249 pub fn $method(self) -> Operation<Handler, Inputs, Args, $kind> {
250 self.out()
251 }
252 )+
253 };
254}
255
256macro_rules! route_methods {
261 ($($method:ident => $marker:ident, $label:literal),+ $(,)?) => {
262 impl<Program> RouteProgram<Program> {
263 $(
264 #[doc = concat!("Records a `", $label, "` selector and typed operation at an exact path.")]
265 pub fn $method<Handler, Inputs, Args, Transform>(
266 self,
267 path: &str,
268 operation: Operation<Handler, Inputs, Args, Transform>,
269 ) -> WithEndpoint<Program, $marker, Handler, Inputs, Args, Transform> {
270 self.method(path, operation)
271 }
272 )+
273 }
274
275 impl<Handler, Inputs, Args, Transform> Operation<Handler, Inputs, Args, Transform> {
276 $(
277 #[doc = concat!("Declares this operation at an exact path, answered under `", $label, "`.")]
278 pub fn $method(self, path: &str) -> Endpoint<$marker, Handler, Inputs, Args, Transform> {
282 self.declare::<$marker>(path)
283 }
284 )+
285 }
286 };
287}
288
289impl<Handler, Inputs, Args, Transform> Operation<Handler, Inputs, Args, Transform> {
290 pub fn declare<Method>(self, path: &str) -> Endpoint<Method, Handler, Inputs, Args, Transform> {
296 Endpoint { path: RoutePath::parse(path), handler: self.handler, marker: PhantomData }
297 }
298}
299
300impl<Handler, Inputs, Args, Transform> Operation<Handler, Inputs, Args, Transform>
301where
302 Inputs: WithAlg,
303 Args: WithAlg,
304{
305 fn with_as<Input, Arg>(self) -> WithInput<Handler, Inputs, Args, Transform, Input, Arg> {
308 Operation { handler: self.handler, marker: PhantomData }
309 }
310
311 pub fn with<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Direct<Input>, Input> {
313 self.with_as::<Direct<Input>, Input>()
314 }
315
316 pub fn path<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Path<Input>, Input> {
318 self.with_as::<Path<Input>, Input>()
319 }
320
321 pub fn query<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Query<Input>, Input>
323 where
324 Input: NamedValuesAlg,
325 {
326 self.with_as::<Query<Input>, Input>()
327 }
328
329 pub fn body<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Body<Input>, Input> {
331 self.with_as::<Body<Input>, Input>()
332 }
333
334 pub fn form<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Form<Input>, Input> {
336 self.with_as::<Form<Input>, Input>()
337 }
338
339 pub fn raw_body<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, RawBody<Input>, Input> {
341 self.with_as::<RawBody<Input>, Input>()
342 }
343
344 pub fn in_header<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Header<Input>, Input>
346 where
347 Input: NamedValuesAlg,
348 {
349 self.with_as::<Header<Input>, Input>()
350 }
351
352 pub fn cookie<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Cookie<Input>, Input>
354 where
355 Input: NamedValuesAlg,
356 {
357 self.with_as::<Cookie<Input>, Input>()
358 }
359
360 pub fn multipart<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Multipart<Input>, Input> {
362 self.with_as::<Multipart<Input>, Input>()
363 }
364
365 pub fn auth<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Auth<Input>, Input>
367 where
368 Input: NamedValuesAlg,
369 {
370 self.with_as::<Auth<Input>, Input>()
371 }
372
373 pub fn context<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Context<Input>, Input> {
375 self.with_as::<Context<Input>, Input>()
376 }
377
378 pub fn out<NewTransform>(self) -> Operation<Handler, Inputs, Args, NewTransform> {
383 Operation { handler: self.handler, marker: PhantomData }
384 }
385
386 with_output_kinds!(output_methods);
387
388 pub fn status<const CODE: u16>(self) -> Operation<Handler, Inputs, Args, StatusOut<Transform, CODE>> {
390 self.out()
391 }
392
393 pub fn out_header<Name>(self) -> Operation<Handler, Inputs, Args, HeaderOut<Transform, Name>> {
398 self.out()
399 }
400
401 pub fn result(self) -> Operation<Handler, Inputs, Args, ResultOut<Transform>> {
406 self.out()
407 }
408}
409
410impl<Program> RouteProgram<Program> {
411 pub fn under(self, prefix: &str) -> Nest<Program> {
417 Nest { prefix: RoutePath::parse(prefix), program: self.0 }
418 }
419
420 pub fn merge<Other>(self, other: RouteProgram<Other>) -> RouteProgram<Merge<Program, Other>> {
425 RouteProgram(Merge { left: self.0, right: other.0 })
426 }
427
428 pub fn nest<Other>(self, prefix: &str, other: RouteProgram<Other>) -> RouteProgram<Merge<Program, Nest<Other>>> {
433 self.merge(RouteProgram(Nest { prefix: RoutePath::parse(prefix), program: other.0 }))
434 }
435
436 pub fn method<Method, Handler, Inputs, Args, Transform>(
441 self,
442 path: &str,
443 operation: Operation<Handler, Inputs, Args, Transform>,
444 ) -> WithEndpoint<Program, Method, Handler, Inputs, Args, Transform> {
445 self.merge(RouteProgram(Endpoint {
446 path: RoutePath::parse(path),
447 handler: operation.handler,
448 marker: PhantomData,
449 }))
450 }
451
452 pub fn into_program(self) -> Program {
454 self.0
455 }
456}
457
458with_http_methods!(route_methods);
459
460impl<Compiler> CompileRouteProgram<Compiler> for Empty
461where
462 Compiler: RouteAlg,
463{
464 type Route = Compiler::Route;
465
466 fn compile_route(self, compiler: &Compiler) -> Self::Route {
467 compiler.initial()
468 }
469}
470
471impl<Compiler, Left, Right, Route> CompileRouteProgram<Compiler> for Merge<Left, Right>
472where
473 Compiler: RouteAlg<Route = Route>,
474 Left: CompileRouteProgram<Compiler, Route = Route>,
475 Right: CompileRouteProgram<Compiler, Route = Route>,
476{
477 type Route = Route;
478
479 fn compile_route(self, compiler: &Compiler) -> Route {
480 compiler.coproduct(self.left.compile_route(compiler), self.right.compile_route(compiler))
481 }
482}
483
484impl<Compiler, Program, Route> CompileRouteProgram<Compiler> for Nest<Program>
485where
486 Compiler: HttpRouteAlg<Route = Route>,
487 Program: CompileRouteProgram<Compiler, Route = Route>,
488{
489 type Route = Route;
490
491 fn compile_route(self, compiler: &Compiler) -> Route {
492 compiler.precompose(compiler.http_prefix(&self.prefix), self.program.compile_route(compiler))
493 }
494}
495
496impl<Compiler, Program> CompileRouteProgram<Compiler> for Named<Program>
497where
498 Program: HttpProgramAlg<Compiler>,
499{
500 type Route = Program::Route;
501
502 fn compile_route(self, compiler: &Compiler) -> Self::Route {
503 self.0.compile_http(compiler)
504 }
505}
506
507impl<Compiler, Method, Handler, Inputs, Args, Transform, Handle> CompileRouteProgram<Compiler>
508 for Endpoint<Method, Handler, Inputs, Args, Transform>
509where
510 Compiler: HttpApiAlg
511 + HandlerContextAlg<Handler::Context, Handle = Handle>
512 + HandlerEndpointAlg<Handle, Inputs::Inputs, Args, Transform, Handler::Output>,
513 Method: HttpMethodAlg,
514 Handler: OperationAlg + ApplyAlg<Handle, Args> + Send + Sync + 'static,
515 Inputs: InterpretInputsAlg<Compiler>,
516{
517 type Route = Compiler::Route;
518
519 fn compile_route(self, compiler: &Compiler) -> Self::Route {
520 let selector = compiler.compose(compiler.http_method(Method::METHOD), compiler.http_path(&self.path));
521 let endpoint = compiler.finish_handler(self.handler);
522
523 compiler.precompose(selector, compiler.lift(endpoint))
524 }
525}