Skip to main content

alux_http/
program.rs

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
10/// Compiles a first-order route program with a concrete interpreter.
11pub trait CompileRouteProgram<Compiler> {
12    /// The route representation produced by `Compiler`.
13    type Route;
14
15    /// Folds the complete first-order program through `compiler`.
16    ///
17    /// Endpoint construction happens here, after composition has preserved all
18    /// handler, input, argument, and output-transform types.
19    fn compile_route(self, compiler: &Compiler) -> Self::Route;
20}
21
22/// Interprets an operation with its declared HTTP input roles and output kind.
23///
24/// A tagless-final specification states this capability for each endpoint it uses. Application,
25/// runtime handles, extraction, and conversion are obligations of the interpreter implementation.
26/// Built-in and downstream output kinds use exactly the same capability.
27///
28/// A JSON declaration cannot be justified by a text-output capability:
29///
30/// ```compile_fail,E0277
31/// use alux_ext::ext;
32/// use alux_http::{HttpOperationAlg, HttpOperationExt, HttpRouteAlg, RouteAlg, RouteAlgExt, Routes, TextOut};
33/// #[ext(name = MissingJsonExt)]
34/// impl<This> This
35/// where
36///     This: HttpRouteAlg
37///         + HttpOperationAlg<(), (), TextOut, Endpoint = <This as RouteAlg>::Endpoint>,
38/// {
39///     fn api(&self) -> Routes<'_, This> {
40///         self.routes().get("/", self.op(()).json())
41///     }
42/// }
43/// ```
44pub trait HttpOperationAlg<Handler, Inputs, Kind> {
45    /// The endpoint representation produced by this interpretation.
46    type Endpoint;
47
48    /// Interprets the operation under its declared input and output roles.
49    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/// Represents the empty route program.
68#[derive(Debug, Default)]
69pub struct Empty;
70
71/// Represents the categorical coproduct of two route programs.
72#[derive(Debug)]
73pub struct Merge<Left, Right> {
74    left: Left,
75    right: Right,
76}
77
78/// Represents a route program nested below an HTTP path prefix.
79#[derive(Debug)]
80pub struct Nest<Program> {
81    prefix: RoutePath,
82    program: Program,
83}
84
85/// Includes a separately named HTTP program in a route program.
86#[derive(Debug)]
87pub struct Named<Program>(Program);
88
89/// Represents an endpoint without choosing an HTTP interpreter.
90#[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/// Carries a typed operation declaration as first-order data.
98#[derive(Debug)]
99pub struct Operation<Handler, Inputs = (), Args = (), Transform = ()> {
100    pub(crate) handler: Handler,
101    marker: PhantomData<fn(Inputs, Args, Transform)>,
102}
103
104/// Carries a typed route program during fluent composition.
105#[derive(Debug)]
106pub struct RouteProgram<Program>(Program);
107
108/// Constructs neutral HTTP route programs.
109#[derive(Debug, Default)]
110pub struct HttpProgramBuilder;
111
112impl<Handler> Operation<Handler> {
113    /// Carries an operation before selecting its HTTP input and output roles.
114    pub fn new(handler: Handler) -> Self {
115        Self { handler, marker: PhantomData }
116    }
117}
118
119/// Marks an input supplied directly by an interpreter.
120pub struct Direct<Input>(PhantomData<Input>);
121
122/// Marks an HTTP path input.
123pub struct Path<Input>(PhantomData<Input>);
124
125/// Marks an HTTP query input.
126pub struct Query<Input>(PhantomData<Input>);
127
128/// Marks an HTTP request-body input.
129pub struct Body<Input>(PhantomData<Input>);
130
131/// Marks a form-encoded HTTP request-body input.
132pub struct Form<Input>(PhantomData<Input>);
133
134/// Marks an HTTP request body taken as it arrived.
135pub struct RawBody<Input>(PhantomData<Input>);
136
137/// Marks an HTTP header input.
138pub struct Header<Input>(PhantomData<Input>);
139
140/// Marks an input read from the cookies a caller sent.
141pub struct Cookie<Input>(PhantomData<Input>);
142
143/// Marks an input read from a request body arriving as parts.
144pub struct Multipart<Input>(PhantomData<Input>);
145
146/// Marks an HTTP authentication input.
147pub struct Auth<Input>(PhantomData<Input>);
148
149/// Marks an endpoint-context input.
150pub struct Context<Input>(PhantomData<Input>);
151
152/// Maps neutral input roles to the input types selected by an interpreter.
153pub trait InterpretInputsAlg<Compiler> {
154    /// The extractor product understood by `Compiler`.
155    type Inputs;
156
157    /// The argument product supplied by these input roles.
158    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    /// Starts an empty, uninterpreted route program.
282    ///
283    /// Subsequent calls record route syntax without requiring any concrete
284    /// framework capabilities.
285    pub fn routes(&self) -> RouteProgram<Empty> {
286        RouteProgram(Empty)
287    }
288
289    /// Wraps a first-order handler operation in a neutral declaration.
290    ///
291    /// Input roles and an output kind can then be attached while the handler's
292    /// result type remains inferred through `ApplyAlg`.
293    pub fn op<Handler>(&self, handler: Handler) -> Operation<Handler> {
294        Operation::new(handler)
295    }
296
297    /// Includes a named HTTP program as an uninterpreted composition node.
298    ///
299    /// The named program is compiled by the same interpreter as its enclosing
300    /// route program when the complete tree is folded.
301    pub fn program<Program>(&self, program: Program) -> RouteProgram<Named<Program>> {
302        RouteProgram(Named(program))
303    }
304}
305
306/// Carries an operation declaration with one additional typed input.
307pub type WithInput<Handler, Inputs, Args, Transform, Extractor, Arg> =
308    Operation<Handler, <Inputs as WithAlg>::With<Extractor>, <Args as WithAlg>::With<Arg>, Transform>;
309
310/// Carries a route program with one additional typed endpoint.
311pub type WithEndpoint<Program, Method, Handler, Inputs, Args, Transform> =
312    RouteProgram<Merge<Program, Endpoint<Method, Handler, Inputs, Args, Transform>>>;
313
314/// States the nine method declarations on both a program and a standalone operation.
315///
316/// An author writes the program form; a declaration read by the `http` macro becomes the operation
317/// form. Emitting both from one list is what keeps the two spellings of a method in step.
318macro_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                ///
341                /// This is the same thing the declaration of that name on `RouteProgram` states,
342                /// for one endpoint standing on its own rather than one inside a composition.
343                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    /// Declares this operation at one path under one method selector, with no program around it.
353    ///
354    /// A program that states many endpoints composes their routes rather than their types, so it
355    /// needs each endpoint on its own. The named declarations below select one method each; this
356    /// states the same thing for a method held as a type parameter.
357    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    // Changes only the declaration's phantom input and argument products while
368    // preserving the first-order handler value.
369    fn with_as<Input, Arg>(self) -> WithInput<Handler, Inputs, Args, Transform, Input, Arg> {
370        Operation { handler: self.handler, marker: PhantomData }
371    }
372
373    /// Records an argument supplied directly in the interpreter's input product.
374    pub fn with<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Direct<Input>, Input> {
375        self.with_as::<Direct<Input>, Input>()
376    }
377
378    /// Records a path extractor whose value becomes the next handler argument.
379    pub fn path<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Path<Input>, Input> {
380        self.with_as::<Path<Input>, Input>()
381    }
382
383    /// Records a query extractor whose value becomes the next handler argument.
384    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    /// Records a request-body extractor whose value becomes the next handler argument.
392    pub fn body<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Body<Input>, Input> {
393        self.with_as::<Body<Input>, Input>()
394    }
395
396    /// Records a form-encoded request-body extractor whose value becomes the next handler argument.
397    pub fn form<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Form<Input>, Input> {
398        self.with_as::<Form<Input>, Input>()
399    }
400
401    /// Records the request body as it arrived, becoming the next handler argument.
402    pub fn raw_body<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, RawBody<Input>, Input> {
403        self.with_as::<RawBody<Input>, Input>()
404    }
405
406    /// Records an incoming header extractor whose value becomes the next handler argument.
407    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    /// Records a cookie extractor whose value becomes the next handler argument.
415    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    /// Records a body arriving as parts, read into the next handler argument.
423    pub fn multipart<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Multipart<Input>, Input> {
424        self.with_as::<Multipart<Input>, Input>()
425    }
426
427    /// Records an authentication extractor whose value becomes the next handler argument.
428    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    /// Records an endpoint-context extractor whose value becomes the next handler argument.
436    pub fn context<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Context<Input>, Input> {
437        self.with_as::<Context<Input>, Input>()
438    }
439
440    // Changes only the declaration's phantom output kind while preserving the first-order handler.
441    fn retag<NewTransform>(self) -> Operation<Handler, Inputs, Args, NewTransform> {
442        Operation { handler: self.handler, marker: PhantomData }
443    }
444
445    /// Closes the declaration with the output kind `Kind`, inside every wrapper already stated.
446    ///
447    /// The kind is interpreted only after the handler result type is known at the compilation
448    /// boundary, so a downstream kind closes a declaration exactly as a built-in one does.
449    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    /// Answers with `CODE`, around what the rest of the declaration states.
457    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    /// Answers with the handler's header value, around what the rest of the declaration states.
465    ///
466    /// The handler answers with the value and then with what the rest states: `(value, rest)`.
467    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    /// Answers with the headers the handler's named product states, around what the rest states.
475    ///
476    /// The output twin of [`Operation::in_header`]: each member of `Headers` is one header, named by
477    /// its member name, and the handler answers with `(headers, rest)`.
478    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    /// Answers with what the rest of the declaration states when the handler succeeded, and with
487    /// what its failure means otherwise.
488    pub fn result(self) -> Operation<Handler, Inputs, Args, OpenWith<Transform, ResultOut<()>>>
489    where
490        Transform: OpenAlg,
491    {
492        self.retag()
493    }
494}
495
496/// Carries a declaration with one more wrapper inside those it already states.
497pub 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    /// Reads this program below an HTTP path prefix, with no program around it.
523    ///
524    /// A program that states many nestings composes their routes rather than their types, so it
525    /// needs each nesting on its own. `RouteProgram::nest` states the same thing inside a
526    /// composition, and is what an author writes.
527    pub fn under(self, prefix: &str) -> Nest<Program> {
528        Nest { prefix: RoutePath::parse(prefix), program: self.0 }
529    }
530
531    /// Records the categorical coproduct of two typed route programs.
532    ///
533    /// Neither side is interpreted, so both complete program types remain
534    /// available to later folds.
535    pub fn merge<Other>(self, other: RouteProgram<Other>) -> RouteProgram<Merge<Program, Other>> {
536        RouteProgram(Merge { left: self.0, right: other.0 })
537    }
538
539    /// Records `other` under `prefix` and merges it into this program.
540    ///
541    /// The prefix is selector precomposition rather than a framework-specific
542    /// router operation.
543    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    /// Records a method selector and typed operation at an exact path.
548    ///
549    /// The named declarations below select one method each and are what an author writes; this
550    /// states the same thing for a method held as a type parameter.
551    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    /// Removes the fluent wrapper and returns the first-order syntax tree.
564    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}