1use crate::{
2 BytesOut, CloseAlg, Connect, Delete, EmptyOut, FileOut, Get, HandlerAlg, HandlerEndpointAlg, Head, HeaderOut,
3 HeadersOut, HtmlOut, HttpInputAlg, HttpMethodAlg, HttpProgramAlg, HttpRouteAlg, JsonOut, NamedValuesAlg, OpenAlg,
4 Options, Patch, Post, Put, RedirectOut, ResultOut, RouteAlg, RoutePath, StatusOut, StreamOut, TextOut, Trace,
5 WithAlg,
6};
7use alux_ext::{ApplyAlg, HandlerContextAlg, OperationAlg};
8use core::marker::PhantomData;
9
10pub trait CompileRouteProgram<Compiler> {
12 type Route;
14
15 fn compile_route(self, compiler: &Compiler) -> Self::Route;
20}
21
22pub trait HttpOperationAlg<Handler, Inputs, Kind> {
45 type Endpoint;
47
48 fn http_operation(&self, handler: Handler) -> Self::Endpoint;
50}
51
52impl<Compiler, Handler, Inputs, Kind, Handle> HttpOperationAlg<Handler, Inputs, Kind> for Compiler
53where
54 Compiler: HandlerAlg
55 + HandlerContextAlg<Handler::Context, Handle = Handle>
56 + HandlerEndpointAlg<Handle, Inputs::Inputs, Inputs::Args, Kind, Handler::Output>,
57 Handler: OperationAlg + ApplyAlg<Handle, Inputs::Args> + Send + Sync + 'static,
58 Inputs: InterpretInputsAlg<Compiler>,
59{
60 type Endpoint = <Compiler as HandlerAlg>::Endpoint;
61
62 fn http_operation(&self, handler: Handler) -> Self::Endpoint {
63 self.finish_handler(handler)
64 }
65}
66
67#[derive(Debug, Default)]
69pub struct Empty;
70
71#[derive(Debug)]
73pub struct Merge<Left, Right> {
74 left: Left,
75 right: Right,
76}
77
78#[derive(Debug)]
80pub struct Nest<Program> {
81 prefix: RoutePath,
82 program: Program,
83}
84
85#[derive(Debug)]
87pub struct Named<Program>(Program);
88
89#[derive(Debug)]
91pub struct Endpoint<Method, Handler, Inputs, Args, Transform> {
92 path: RoutePath,
93 handler: Handler,
94 marker: PhantomData<fn(Method, Inputs, Args, Transform)>,
95}
96
97#[derive(Debug)]
99pub struct Operation<Handler, Inputs = (), Args = (), Transform = ()> {
100 pub(crate) handler: Handler,
101 marker: PhantomData<fn(Inputs, Args, Transform)>,
102}
103
104#[derive(Debug)]
106pub struct RouteProgram<Program>(Program);
107
108#[derive(Debug, Default)]
110pub struct HttpProgramBuilder;
111
112impl<Handler> Operation<Handler> {
113 pub fn new(handler: Handler) -> Self {
115 Self { handler, marker: PhantomData }
116 }
117}
118
119pub struct Direct<Input>(PhantomData<Input>);
121
122pub struct Path<Input>(PhantomData<Input>);
124
125pub struct Query<Input>(PhantomData<Input>);
127
128pub struct Body<Input>(PhantomData<Input>);
130
131pub struct Form<Input>(PhantomData<Input>);
133
134pub struct RawBody<Input>(PhantomData<Input>);
136
137pub struct Header<Input>(PhantomData<Input>);
139
140pub struct Cookie<Input>(PhantomData<Input>);
142
143pub struct Multipart<Input>(PhantomData<Input>);
145
146pub struct Auth<Input>(PhantomData<Input>);
148
149pub struct Context<Input>(PhantomData<Input>);
151
152pub trait InterpretInputsAlg<Compiler> {
154 type Inputs;
156
157 type Args;
159}
160
161impl<Compiler> InterpretInputsAlg<Compiler> for () {
162 type Inputs = ();
163 type Args = ();
164}
165
166impl<Compiler, Input> InterpretInputsAlg<Compiler> for Direct<Input> {
167 type Inputs = Input;
168 type Args = Input;
169}
170
171impl<Compiler, Input> InterpretInputsAlg<Compiler> for Path<Input>
172where
173 Compiler: HttpInputAlg,
174{
175 type Inputs = Compiler::Path<Input>;
176 type Args = Input;
177}
178
179impl<Compiler, Input> InterpretInputsAlg<Compiler> for Query<Input>
180where
181 Compiler: HttpInputAlg,
182{
183 type Inputs = Compiler::Query<Input>;
184 type Args = Input;
185}
186
187impl<Compiler, Input> InterpretInputsAlg<Compiler> for Body<Input>
188where
189 Compiler: HttpInputAlg,
190{
191 type Inputs = Compiler::Body<Input>;
192 type Args = Input;
193}
194
195impl<Compiler, Input> InterpretInputsAlg<Compiler> for Form<Input>
196where
197 Compiler: HttpInputAlg,
198{
199 type Inputs = Compiler::Form<Input>;
200 type Args = Input;
201}
202
203impl<Compiler, Input> InterpretInputsAlg<Compiler> for RawBody<Input>
204where
205 Compiler: HttpInputAlg,
206{
207 type Inputs = Compiler::RawBody<Input>;
208 type Args = Input;
209}
210
211impl<Compiler, Input> InterpretInputsAlg<Compiler> for Header<Input>
212where
213 Compiler: HttpInputAlg,
214{
215 type Inputs = Compiler::Header<Input>;
216 type Args = Input;
217}
218
219impl<Compiler, Input> InterpretInputsAlg<Compiler> for Cookie<Input>
220where
221 Compiler: HttpInputAlg,
222{
223 type Inputs = Compiler::Cookie<Input>;
224 type Args = Input;
225}
226
227impl<Compiler, Input> InterpretInputsAlg<Compiler> for Multipart<Input>
228where
229 Compiler: HttpInputAlg,
230{
231 type Inputs = Compiler::Multipart<Input>;
232 type Args = Input;
233}
234
235impl<Compiler, Input> InterpretInputsAlg<Compiler> for Auth<Input>
236where
237 Compiler: HttpInputAlg,
238{
239 type Inputs = Compiler::Auth<Input>;
240 type Args = Input;
241}
242
243impl<Compiler, Input> InterpretInputsAlg<Compiler> for Context<Input>
244where
245 Compiler: HttpInputAlg,
246{
247 type Inputs = Compiler::Context<Input>;
248 type Args = Input;
249}
250
251macro_rules! interpret_inputs {
252 ($($input:ident),+ $(,)?) => {
253 impl<Compiler, $($input),+> InterpretInputsAlg<Compiler> for ($($input,)+)
254 where
255 $($input: InterpretInputsAlg<Compiler>,)+
256 {
257 type Inputs = ($($input::Inputs,)+);
258 type Args = ($($input::Args,)+);
259 }
260 };
261}
262
263interpret_inputs!(I1);
264interpret_inputs!(I1, I2);
265interpret_inputs!(I1, I2, I3);
266interpret_inputs!(I1, I2, I3, I4);
267interpret_inputs!(I1, I2, I3, I4, I5);
268interpret_inputs!(I1, I2, I3, I4, I5, I6);
269interpret_inputs!(I1, I2, I3, I4, I5, I6, I7);
270interpret_inputs!(I1, I2, I3, I4, I5, I6, I7, I8);
271interpret_inputs!(I1, I2, I3, I4, I5, I6, I7, I8, I9);
272interpret_inputs!(I1, I2, I3, I4, I5, I6, I7, I8, I9, I10);
273interpret_inputs!(I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11);
274interpret_inputs!(I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12);
275interpret_inputs!(I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13);
276interpret_inputs!(I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14);
277interpret_inputs!(I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15);
278interpret_inputs!(I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15, I16);
279
280impl HttpProgramBuilder {
281 pub fn routes(&self) -> RouteProgram<Empty> {
286 RouteProgram(Empty)
287 }
288
289 pub fn op<Handler>(&self, handler: Handler) -> Operation<Handler> {
294 Operation::new(handler)
295 }
296
297 pub fn program<Program>(&self, program: Program) -> RouteProgram<Named<Program>> {
302 RouteProgram(Named(program))
303 }
304}
305
306pub type WithInput<Handler, Inputs, Args, Transform, Extractor, Arg> =
308 Operation<Handler, <Inputs as WithAlg>::With<Extractor>, <Args as WithAlg>::With<Arg>, Transform>;
309
310pub type WithEndpoint<Program, Method, Handler, Inputs, Args, Transform> =
312 RouteProgram<Merge<Program, Endpoint<Method, Handler, Inputs, Args, Transform>>>;
313
314macro_rules! route_methods {
319 ($($method:ident => $marker:ident, $label:literal),+ $(,)?) => {
320 impl<Program> RouteProgram<Program> {
321 $(
322 #[doc = concat!("Records a `", $label, "` selector and typed operation at an exact path.")]
323 pub fn $method<Handler, Inputs, Args, Transform>(
324 self,
325 path: &str,
326 operation: Operation<Handler, Inputs, Args, Transform>,
327 ) -> WithEndpoint<Program, $marker, Handler, Inputs, Args, Transform> {
328 self.method(path, operation)
329 }
330 )+
331 }
332
333 impl<Handler, Inputs, Args, Transform> Operation<Handler, Inputs, Args, Transform>
334 where
335 Inputs: WithAlg,
336 Args: WithAlg,
337 {
338 $(
339 #[doc = concat!("Declares this operation at an exact path, answered under `", $label, "`.")]
340 pub fn $method(self, path: &str) -> Endpoint<$marker, Handler, Inputs, Args, Transform> {
344 self.declare::<$marker>(path)
345 }
346 )+
347 }
348 };
349}
350
351impl<Handler, Inputs, Args, Transform> Operation<Handler, Inputs, Args, Transform> {
352 pub fn declare<Method>(self, path: &str) -> Endpoint<Method, Handler, Inputs, Args, Transform> {
358 Endpoint { path: RoutePath::parse(path), handler: self.handler, marker: PhantomData }
359 }
360}
361
362impl<Handler, Inputs, Args, Transform> Operation<Handler, Inputs, Args, Transform>
363where
364 Inputs: WithAlg,
365 Args: WithAlg,
366{
367 fn with_as<Input, Arg>(self) -> WithInput<Handler, Inputs, Args, Transform, Input, Arg> {
370 Operation { handler: self.handler, marker: PhantomData }
371 }
372
373 pub fn with<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Direct<Input>, Input> {
375 self.with_as::<Direct<Input>, Input>()
376 }
377
378 pub fn path<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Path<Input>, Input> {
380 self.with_as::<Path<Input>, Input>()
381 }
382
383 pub fn query<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Query<Input>, Input>
385 where
386 Input: NamedValuesAlg,
387 {
388 self.with_as::<Query<Input>, Input>()
389 }
390
391 pub fn body<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Body<Input>, Input> {
393 self.with_as::<Body<Input>, Input>()
394 }
395
396 pub fn form<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Form<Input>, Input> {
398 self.with_as::<Form<Input>, Input>()
399 }
400
401 pub fn raw_body<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, RawBody<Input>, Input> {
403 self.with_as::<RawBody<Input>, Input>()
404 }
405
406 pub fn in_header<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Header<Input>, Input>
408 where
409 Input: NamedValuesAlg,
410 {
411 self.with_as::<Header<Input>, Input>()
412 }
413
414 pub fn cookie<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Cookie<Input>, Input>
416 where
417 Input: NamedValuesAlg,
418 {
419 self.with_as::<Cookie<Input>, Input>()
420 }
421
422 pub fn multipart<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Multipart<Input>, Input> {
424 self.with_as::<Multipart<Input>, Input>()
425 }
426
427 pub fn auth<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Auth<Input>, Input>
429 where
430 Input: NamedValuesAlg,
431 {
432 self.with_as::<Auth<Input>, Input>()
433 }
434
435 pub fn context<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Context<Input>, Input> {
437 self.with_as::<Context<Input>, Input>()
438 }
439
440 fn retag<NewTransform>(self) -> Operation<Handler, Inputs, Args, NewTransform> {
442 Operation { handler: self.handler, marker: PhantomData }
443 }
444
445 pub fn out<Kind>(self) -> Operation<Handler, Inputs, Args, <Transform as CloseAlg<Kind>>::Closed>
450 where
451 Transform: CloseAlg<Kind>,
452 {
453 self.retag()
454 }
455
456 pub fn status<const CODE: u16>(self) -> Operation<Handler, Inputs, Args, OpenWith<Transform, StatusOut<(), CODE>>>
458 where
459 Transform: OpenAlg,
460 {
461 self.retag()
462 }
463
464 pub fn out_header<Name>(self) -> Operation<Handler, Inputs, Args, OpenWith<Transform, HeaderOut<(), Name>>>
468 where
469 Transform: OpenAlg,
470 {
471 self.retag()
472 }
473
474 pub fn out_headers<Headers>(self) -> Operation<Handler, Inputs, Args, OpenWith<Transform, HeadersOut<(), Headers>>>
479 where
480 Transform: OpenAlg,
481 Headers: NamedValuesAlg,
482 {
483 self.retag()
484 }
485
486 pub fn result(self) -> Operation<Handler, Inputs, Args, OpenWith<Transform, ResultOut<()>>>
489 where
490 Transform: OpenAlg,
491 {
492 self.retag()
493 }
494}
495
496pub type OpenWith<Transform, Wrapper> = <Transform as OpenAlg>::With<Wrapper>;
498
499macro_rules! output_methods {
500 ($($method:ident => $kind:ident, $alg:ident, $selected:ident, $meaning:literal),+ $(,)?) => {
501 impl<Handler, Inputs, Args, Transform> Operation<Handler, Inputs, Args, Transform>
502 where
503 Inputs: WithAlg,
504 Args: WithAlg,
505 {
506 $(
507 #[doc = concat!("Closes the declaration with ", $meaning, " output meaning.")]
508 pub fn $method(self) -> Operation<Handler, Inputs, Args, <Transform as CloseAlg<$kind>>::Closed>
509 where
510 Transform: CloseAlg<$kind>,
511 {
512 self.out()
513 }
514 )+
515 }
516 };
517}
518
519with_output_kinds!(output_methods);
520
521impl<Program> RouteProgram<Program> {
522 pub fn under(self, prefix: &str) -> Nest<Program> {
528 Nest { prefix: RoutePath::parse(prefix), program: self.0 }
529 }
530
531 pub fn merge<Other>(self, other: RouteProgram<Other>) -> RouteProgram<Merge<Program, Other>> {
536 RouteProgram(Merge { left: self.0, right: other.0 })
537 }
538
539 pub fn nest<Other>(self, prefix: &str, other: RouteProgram<Other>) -> RouteProgram<Merge<Program, Nest<Other>>> {
544 self.merge(RouteProgram(Nest { prefix: RoutePath::parse(prefix), program: other.0 }))
545 }
546
547 pub fn method<Method, Handler, Inputs, Args, Transform>(
552 self,
553 path: &str,
554 operation: Operation<Handler, Inputs, Args, Transform>,
555 ) -> WithEndpoint<Program, Method, Handler, Inputs, Args, Transform> {
556 self.merge(RouteProgram(Endpoint {
557 path: RoutePath::parse(path),
558 handler: operation.handler,
559 marker: PhantomData,
560 }))
561 }
562
563 pub fn into_program(self) -> Program {
565 self.0
566 }
567}
568
569with_http_methods!(route_methods);
570
571impl<Compiler> CompileRouteProgram<Compiler> for Empty
572where
573 Compiler: RouteAlg,
574{
575 type Route = Compiler::Route;
576
577 fn compile_route(self, compiler: &Compiler) -> Self::Route {
578 compiler.initial()
579 }
580}
581
582impl<Compiler, Left, Right, Route> CompileRouteProgram<Compiler> for Merge<Left, Right>
583where
584 Compiler: RouteAlg<Route = Route>,
585 Left: CompileRouteProgram<Compiler, Route = Route>,
586 Right: CompileRouteProgram<Compiler, Route = Route>,
587{
588 type Route = Route;
589
590 fn compile_route(self, compiler: &Compiler) -> Route {
591 compiler.coproduct(self.left.compile_route(compiler), self.right.compile_route(compiler))
592 }
593}
594
595impl<Compiler, Program, Route> CompileRouteProgram<Compiler> for Nest<Program>
596where
597 Compiler: HttpRouteAlg<Route = Route>,
598 Program: CompileRouteProgram<Compiler, Route = Route>,
599{
600 type Route = Route;
601
602 fn compile_route(self, compiler: &Compiler) -> Route {
603 compiler.precompose(compiler.http_prefix(&self.prefix), self.program.compile_route(compiler))
604 }
605}
606
607impl<Compiler, Program> CompileRouteProgram<Compiler> for Named<Program>
608where
609 Program: HttpProgramAlg<Compiler>,
610{
611 type Route = Program::Route;
612
613 fn compile_route(self, compiler: &Compiler) -> Self::Route {
614 self.0.compile_http(compiler)
615 }
616}
617
618impl<Compiler, Method, Handler, Inputs, Args, Transform> CompileRouteProgram<Compiler>
619 for Endpoint<Method, Handler, Inputs, Args, Transform>
620where
621 Compiler: HttpRouteAlg + HttpOperationAlg<Handler, Inputs, Transform, Endpoint = <Compiler as RouteAlg>::Endpoint>,
622 Method: HttpMethodAlg,
623{
624 type Route = Compiler::Route;
625
626 fn compile_route(self, compiler: &Compiler) -> Self::Route {
627 let selector = compiler.compose(compiler.http_method(Method::METHOD), compiler.http_path(&self.path));
628 let endpoint = compiler.http_operation(self.handler);
629 compiler.precompose(selector, compiler.lift(endpoint))
630 }
631}