1use crate::{
2 FileOut, HandlerEndpointAlg, HttpApiAlg, HttpInputAlg, HttpProgramAlg, HttpRouteAlg, JsonOut, RouteAlg, WithAlg,
3};
4use alux_ext::{ApplyAlg, HandlerContextAlg, OperationAlg};
5use core::marker::PhantomData;
6
7pub trait CompileRouteProgram<Compiler> {
9 type Route;
11
12 fn compile_route(self, compiler: &Compiler) -> Self::Route;
17}
18
19#[derive(Debug, Default)]
21pub struct Empty;
22
23#[derive(Debug)]
25pub struct Merge<Left, Right> {
26 left: Left,
27 right: Right,
28}
29
30#[derive(Debug)]
32pub struct Nest<Program> {
33 prefix: String,
34 program: Program,
35}
36
37#[derive(Debug)]
39pub struct Named<Program>(Program);
40
41#[derive(Debug)]
43pub struct Get;
44
45#[derive(Debug)]
47pub struct Post;
48
49#[derive(Debug)]
51pub struct Endpoint<Method, Handler, Inputs, Args, Transform> {
52 path: String,
53 handler: Handler,
54 marker: PhantomData<fn(Method, Inputs, Args, Transform)>,
55}
56
57#[derive(Debug)]
59pub struct Operation<Handler, Inputs = (), Args = (), Transform = ()> {
60 handler: Handler,
61 marker: PhantomData<fn(Inputs, Args, Transform)>,
62}
63
64#[derive(Debug)]
66pub struct RouteProgram<Program>(Program);
67
68#[derive(Debug, Default)]
70pub struct HttpProgramBuilder;
71
72pub struct Direct<Input>(PhantomData<Input>);
74
75pub struct Path<Input>(PhantomData<Input>);
77
78pub struct Query<Input>(PhantomData<Input>);
80
81pub struct Body<Input>(PhantomData<Input>);
83
84pub struct Header<Input>(PhantomData<Input>);
86
87pub struct Auth<Input>(PhantomData<Input>);
89
90pub struct Context<Input>(PhantomData<Input>);
92
93pub trait InterpretInputsAlg<Compiler> {
95 type Inputs;
97}
98
99impl<Compiler> InterpretInputsAlg<Compiler> for () {
100 type Inputs = ();
101}
102
103impl<Compiler, Input> InterpretInputsAlg<Compiler> for Direct<Input> {
104 type Inputs = Input;
105}
106
107impl<Compiler, Input> InterpretInputsAlg<Compiler> for Path<Input>
108where
109 Compiler: HttpInputAlg,
110{
111 type Inputs = Compiler::Path<Input>;
112}
113
114impl<Compiler, Input> InterpretInputsAlg<Compiler> for Query<Input>
115where
116 Compiler: HttpInputAlg,
117{
118 type Inputs = Compiler::Query<Input>;
119}
120
121impl<Compiler, Input> InterpretInputsAlg<Compiler> for Body<Input>
122where
123 Compiler: HttpInputAlg,
124{
125 type Inputs = Compiler::Body<Input>;
126}
127
128impl<Compiler, Input> InterpretInputsAlg<Compiler> for Header<Input>
129where
130 Compiler: HttpInputAlg,
131{
132 type Inputs = Compiler::Header<Input>;
133}
134
135impl<Compiler, Input> InterpretInputsAlg<Compiler> for Auth<Input>
136where
137 Compiler: HttpInputAlg,
138{
139 type Inputs = Compiler::Auth<Input>;
140}
141
142impl<Compiler, Input> InterpretInputsAlg<Compiler> for Context<Input>
143where
144 Compiler: HttpInputAlg,
145{
146 type Inputs = Compiler::Context<Input>;
147}
148
149macro_rules! interpret_inputs {
150 ($($input:ident),+ $(,)?) => {
151 impl<Compiler, $($input),+> InterpretInputsAlg<Compiler> for ($($input,)+)
152 where
153 $($input: InterpretInputsAlg<Compiler>,)+
154 {
155 type Inputs = ($($input::Inputs,)+);
156 }
157 };
158}
159
160interpret_inputs!(I1);
161interpret_inputs!(I1, I2);
162interpret_inputs!(I1, I2, I3);
163interpret_inputs!(I1, I2, I3, I4);
164interpret_inputs!(I1, I2, I3, I4, I5);
165interpret_inputs!(I1, I2, I3, I4, I5, I6);
166interpret_inputs!(I1, I2, I3, I4, I5, I6, I7);
167interpret_inputs!(I1, I2, I3, I4, I5, I6, I7, I8);
168
169impl HttpProgramBuilder {
170 pub fn routes(&self) -> RouteProgram<Empty> {
175 RouteProgram(Empty)
176 }
177
178 pub fn op<Handler>(&self, handler: Handler) -> Operation<Handler> {
183 Operation { handler, marker: PhantomData }
184 }
185
186 pub fn program<Program>(&self, program: Program) -> RouteProgram<Named<Program>> {
191 RouteProgram(Named(program))
192 }
193}
194
195pub type WithInput<Handler, Inputs, Args, Transform, Extractor, Arg> =
197 Operation<Handler, <Inputs as WithAlg>::With<Extractor>, <Args as WithAlg>::With<Arg>, Transform>;
198
199pub type WithEndpoint<Program, Method, Handler, Inputs, Args, Transform> =
201 RouteProgram<Merge<Program, Endpoint<Method, Handler, Inputs, Args, Transform>>>;
202
203impl<Handler, Inputs, Args, Transform> Operation<Handler, Inputs, Args, Transform> {
204 pub fn declare<Method>(self, path: &str) -> Endpoint<Method, Handler, Inputs, Args, Transform> {
210 Endpoint { path: path.into(), handler: self.handler, marker: PhantomData }
211 }
212}
213
214impl<Handler, Inputs, Args, Transform> Operation<Handler, Inputs, Args, Transform>
215where
216 Inputs: WithAlg,
217 Args: WithAlg,
218{
219 fn with_as<Input, Arg>(self) -> WithInput<Handler, Inputs, Args, Transform, Input, Arg> {
222 Operation { handler: self.handler, marker: PhantomData }
223 }
224
225 pub fn with<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Direct<Input>, Input> {
227 self.with_as::<Direct<Input>, Input>()
228 }
229
230 pub fn path<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Path<Input>, Input> {
232 self.with_as::<Path<Input>, Input>()
233 }
234
235 pub fn query<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Query<Input>, Input> {
237 self.with_as::<Query<Input>, Input>()
238 }
239
240 pub fn body<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Body<Input>, Input> {
242 self.with_as::<Body<Input>, Input>()
243 }
244
245 pub fn header<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Header<Input>, Input> {
247 self.with_as::<Header<Input>, Input>()
248 }
249
250 pub fn auth<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Auth<Input>, Input> {
252 self.with_as::<Auth<Input>, Input>()
253 }
254
255 pub fn context<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Context<Input>, Input> {
257 self.with_as::<Context<Input>, Input>()
258 }
259
260 pub fn out<NewTransform>(self) -> Operation<Handler, Inputs, Args, NewTransform> {
265 Operation { handler: self.handler, marker: PhantomData }
266 }
267
268 pub fn json(self) -> Operation<Handler, Inputs, Args, JsonOut> {
270 self.out()
271 }
272
273 pub fn file(self) -> Operation<Handler, Inputs, Args, FileOut> {
275 self.out()
276 }
277}
278
279impl<Program> RouteProgram<Program> {
280 pub fn under(self, prefix: &str) -> Nest<Program> {
286 Nest { prefix: prefix.into(), program: self.0 }
287 }
288
289 pub fn merge<Other>(self, other: RouteProgram<Other>) -> RouteProgram<Merge<Program, Other>> {
294 RouteProgram(Merge { left: self.0, right: other.0 })
295 }
296
297 pub fn nest<Other>(self, prefix: &str, other: RouteProgram<Other>) -> RouteProgram<Merge<Program, Nest<Other>>> {
302 self.merge(RouteProgram(Nest { prefix: prefix.into(), program: other.0 }))
303 }
304
305 pub fn get<Handler, Inputs, Args, Transform>(
307 self,
308 path: &str,
309 operation: Operation<Handler, Inputs, Args, Transform>,
310 ) -> WithEndpoint<Program, Get, Handler, Inputs, Args, Transform> {
311 self.merge(RouteProgram(Endpoint { path: path.into(), handler: operation.handler, marker: PhantomData }))
312 }
313
314 pub fn post<Handler, Inputs, Args, Transform>(
316 self,
317 path: &str,
318 operation: Operation<Handler, Inputs, Args, Transform>,
319 ) -> WithEndpoint<Program, Post, Handler, Inputs, Args, Transform> {
320 self.merge(RouteProgram(Endpoint { path: path.into(), handler: operation.handler, marker: PhantomData }))
321 }
322
323 pub fn into_program(self) -> Program {
325 self.0
326 }
327}
328
329impl<Compiler> CompileRouteProgram<Compiler> for Empty
330where
331 Compiler: RouteAlg,
332{
333 type Route = Compiler::Route;
334
335 fn compile_route(self, compiler: &Compiler) -> Self::Route {
336 compiler.initial()
337 }
338}
339
340impl<Compiler, Left, Right, Route> CompileRouteProgram<Compiler> for Merge<Left, Right>
341where
342 Compiler: RouteAlg<Route = Route>,
343 Left: CompileRouteProgram<Compiler, Route = Route>,
344 Right: CompileRouteProgram<Compiler, Route = Route>,
345{
346 type Route = Route;
347
348 fn compile_route(self, compiler: &Compiler) -> Route {
349 compiler.coproduct(self.left.compile_route(compiler), self.right.compile_route(compiler))
350 }
351}
352
353impl<Compiler, Program, Route> CompileRouteProgram<Compiler> for Nest<Program>
354where
355 Compiler: HttpRouteAlg<Route = Route>,
356 Program: CompileRouteProgram<Compiler, Route = Route>,
357{
358 type Route = Route;
359
360 fn compile_route(self, compiler: &Compiler) -> Route {
361 compiler.precompose(compiler.http_prefix(&self.prefix), self.program.compile_route(compiler))
362 }
363}
364
365impl<Compiler, Program> CompileRouteProgram<Compiler> for Named<Program>
366where
367 Program: HttpProgramAlg<Compiler>,
368{
369 type Route = Program::Route;
370
371 fn compile_route(self, compiler: &Compiler) -> Self::Route {
372 self.0.compile_http(compiler)
373 }
374}
375
376trait HttpMethodAlg<Compiler> {
377 fn selector(compiler: &Compiler) -> <Compiler as RouteAlg>::Selector
378 where
379 Compiler: HttpApiAlg;
380}
381
382impl<Compiler> HttpMethodAlg<Compiler> for Get {
383 fn selector(compiler: &Compiler) -> <Compiler as RouteAlg>::Selector
384 where
385 Compiler: HttpApiAlg,
386 {
387 compiler.http_get()
388 }
389}
390
391impl<Compiler> HttpMethodAlg<Compiler> for Post {
392 fn selector(compiler: &Compiler) -> <Compiler as RouteAlg>::Selector
393 where
394 Compiler: HttpApiAlg,
395 {
396 compiler.http_post()
397 }
398}
399
400impl<Compiler, Method, Handler, Inputs, Args, Transform, Handle> CompileRouteProgram<Compiler>
401 for Endpoint<Method, Handler, Inputs, Args, Transform>
402where
403 Compiler: HttpApiAlg
404 + HandlerContextAlg<Handler::Context, Handle = Handle>
405 + HandlerEndpointAlg<Handle, Inputs::Inputs, Args, Transform, Handler::Output>,
406 Method: HttpMethodAlg<Compiler>,
407 Handler: OperationAlg + ApplyAlg<Handle, Args> + Send + Sync + 'static,
408 Inputs: InterpretInputsAlg<Compiler>,
409{
410 type Route = Compiler::Route;
411
412 fn compile_route(self, compiler: &Compiler) -> Self::Route {
413 let selector = compiler.compose(Method::selector(compiler), compiler.http_path(&self.path));
414 let endpoint = compiler.finish_handler(self.handler);
415
416 compiler.precompose(selector, compiler.lift(endpoint))
417 }
418}