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>
204where
205 Inputs: WithAlg,
206 Args: WithAlg,
207{
208 fn with_as<Input, Arg>(self) -> WithInput<Handler, Inputs, Args, Transform, Input, Arg> {
211 Operation { handler: self.handler, marker: PhantomData }
212 }
213
214 pub fn with<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Direct<Input>, Input> {
216 self.with_as::<Direct<Input>, Input>()
217 }
218
219 pub fn path<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Path<Input>, Input> {
221 self.with_as::<Path<Input>, Input>()
222 }
223
224 pub fn query<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Query<Input>, Input> {
226 self.with_as::<Query<Input>, Input>()
227 }
228
229 pub fn body<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Body<Input>, Input> {
231 self.with_as::<Body<Input>, Input>()
232 }
233
234 pub fn header<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Header<Input>, Input> {
236 self.with_as::<Header<Input>, Input>()
237 }
238
239 pub fn auth<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Auth<Input>, Input> {
241 self.with_as::<Auth<Input>, Input>()
242 }
243
244 pub fn context<Input>(self) -> WithInput<Handler, Inputs, Args, Transform, Context<Input>, Input> {
246 self.with_as::<Context<Input>, Input>()
247 }
248
249 pub fn out<NewTransform>(self) -> Operation<Handler, Inputs, Args, NewTransform> {
254 Operation { handler: self.handler, marker: PhantomData }
255 }
256
257 pub fn json(self) -> Operation<Handler, Inputs, Args, JsonOut> {
259 self.out()
260 }
261
262 pub fn file(self) -> Operation<Handler, Inputs, Args, FileOut> {
264 self.out()
265 }
266}
267
268impl<Program> RouteProgram<Program> {
269 pub fn merge<Other>(self, other: RouteProgram<Other>) -> RouteProgram<Merge<Program, Other>> {
274 RouteProgram(Merge { left: self.0, right: other.0 })
275 }
276
277 pub fn nest<Other>(self, prefix: &str, other: RouteProgram<Other>) -> RouteProgram<Merge<Program, Nest<Other>>> {
282 self.merge(RouteProgram(Nest { prefix: prefix.into(), program: other.0 }))
283 }
284
285 pub fn get<Handler, Inputs, Args, Transform>(
287 self,
288 path: &str,
289 operation: Operation<Handler, Inputs, Args, Transform>,
290 ) -> WithEndpoint<Program, Get, Handler, Inputs, Args, Transform> {
291 self.merge(RouteProgram(Endpoint { path: path.into(), handler: operation.handler, marker: PhantomData }))
292 }
293
294 pub fn post<Handler, Inputs, Args, Transform>(
296 self,
297 path: &str,
298 operation: Operation<Handler, Inputs, Args, Transform>,
299 ) -> WithEndpoint<Program, Post, Handler, Inputs, Args, Transform> {
300 self.merge(RouteProgram(Endpoint { path: path.into(), handler: operation.handler, marker: PhantomData }))
301 }
302
303 pub fn into_program(self) -> Program {
305 self.0
306 }
307}
308
309impl<Compiler> CompileRouteProgram<Compiler> for Empty
310where
311 Compiler: RouteAlg,
312{
313 type Route = Compiler::Route;
314
315 fn compile_route(self, compiler: &Compiler) -> Self::Route {
316 compiler.initial()
317 }
318}
319
320impl<Compiler, Left, Right, Route> CompileRouteProgram<Compiler> for Merge<Left, Right>
321where
322 Compiler: RouteAlg<Route = Route>,
323 Left: CompileRouteProgram<Compiler, Route = Route>,
324 Right: CompileRouteProgram<Compiler, Route = Route>,
325{
326 type Route = Route;
327
328 fn compile_route(self, compiler: &Compiler) -> Route {
329 compiler.coproduct(self.left.compile_route(compiler), self.right.compile_route(compiler))
330 }
331}
332
333impl<Compiler, Program, Route> CompileRouteProgram<Compiler> for Nest<Program>
334where
335 Compiler: HttpRouteAlg<Route = Route>,
336 Program: CompileRouteProgram<Compiler, Route = Route>,
337{
338 type Route = Route;
339
340 fn compile_route(self, compiler: &Compiler) -> Route {
341 compiler.precompose(compiler.http_prefix(&self.prefix), self.program.compile_route(compiler))
342 }
343}
344
345impl<Compiler, Program> CompileRouteProgram<Compiler> for Named<Program>
346where
347 Program: HttpProgramAlg<Compiler>,
348{
349 type Route = Program::Route;
350
351 fn compile_route(self, compiler: &Compiler) -> Self::Route {
352 self.0.compile_http(compiler)
353 }
354}
355
356trait HttpMethodAlg<Compiler> {
357 fn selector(compiler: &Compiler) -> <Compiler as RouteAlg>::Selector
358 where
359 Compiler: HttpApiAlg;
360}
361
362impl<Compiler> HttpMethodAlg<Compiler> for Get {
363 fn selector(compiler: &Compiler) -> <Compiler as RouteAlg>::Selector
364 where
365 Compiler: HttpApiAlg,
366 {
367 compiler.http_get()
368 }
369}
370
371impl<Compiler> HttpMethodAlg<Compiler> for Post {
372 fn selector(compiler: &Compiler) -> <Compiler as RouteAlg>::Selector
373 where
374 Compiler: HttpApiAlg,
375 {
376 compiler.http_post()
377 }
378}
379
380impl<Compiler, Method, Handler, Inputs, Args, Transform, Handle> CompileRouteProgram<Compiler>
381 for Endpoint<Method, Handler, Inputs, Args, Transform>
382where
383 Compiler: HttpApiAlg
384 + HandlerContextAlg<Handler::Context, Handle = Handle>
385 + HandlerEndpointAlg<Handle, Inputs::Inputs, Args, Transform, Handler::Output>,
386 Method: HttpMethodAlg<Compiler>,
387 Handler: OperationAlg + ApplyAlg<Handle, Args> + Send + Sync + 'static,
388 Inputs: InterpretInputsAlg<Compiler>,
389{
390 type Route = Compiler::Route;
391
392 fn compile_route(self, compiler: &Compiler) -> Self::Route {
393 let selector = compiler.compose(Method::selector(compiler), compiler.http_path(&self.path));
394 let endpoint = compiler.finish_handler(self.handler);
395
396 compiler.precompose(selector, compiler.lift(endpoint))
397 }
398}