1use alux_ext::{ApplyAlg, ext};
2
3pub trait HandlerAlg {
5 type Endpoint;
7}
8
9pub trait HandlerEndpointAlg<Context, Inputs, Args, Transform, Output> {
11 fn finish_handler<Handler>(&self, handler: Handler) -> Self::Endpoint
16 where
17 Self: HandlerAlg,
18 Handler: ApplyAlg<Context, Args, Output = Output> + Send + Sync + 'static;
19}
20
21pub trait HttpProgramAlg<Compiler> {
23 type Route;
25
26 fn compile_http(self, compiler: &Compiler) -> Self::Route;
28}
29
30pub trait SelectorAlg {
32 type Selector;
34
35 fn identity(&self) -> Self::Selector;
37
38 fn compose(&self, first: Self::Selector, second: Self::Selector) -> Self::Selector;
40}
41
42pub trait RouteAlg {
44 type Route;
46 type Selector;
48 type Endpoint;
50
51 fn initial(&self) -> Self::Route;
53
54 fn coproduct(&self, left: Self::Route, right: Self::Route) -> Self::Route;
56
57 fn precompose(&self, selector: Self::Selector, route: Self::Route) -> Self::Route;
59
60 fn lift(&self, endpoint: Self::Endpoint) -> Self::Route;
62}
63
64pub trait HttpInputAlg {
66 type Path<Input>;
68 type Query<Input>;
70 type Body<Input>;
72 type Header<Input>;
74 type Auth<Input>;
76 type Context<Input>;
78}
79
80pub trait HttpSelectorAlg {
82 type Selector;
84
85 fn http_get(&self) -> Self::Selector;
87
88 fn http_post(&self) -> Self::Selector;
90
91 fn http_path(&self, path: &str) -> Self::Selector;
93
94 fn http_prefix(&self, prefix: &str) -> Self::Selector;
96}
97
98pub trait HttpRouteAlg:
103 SelectorAlg
104 + HttpSelectorAlg<Selector = <Self as SelectorAlg>::Selector>
105 + RouteAlg<Selector = <Self as SelectorAlg>::Selector>
106{
107}
108
109impl<This> HttpRouteAlg for This where
110 This: SelectorAlg
111 + HttpSelectorAlg<Selector = <This as SelectorAlg>::Selector>
112 + RouteAlg<Selector = <This as SelectorAlg>::Selector>
113{
114}
115
116pub trait HttpApiAlg:
121 HandlerAlg + HttpInputAlg + HttpRouteAlg + RouteAlg<Endpoint = <Self as HandlerAlg>::Endpoint>
122{
123}
124
125impl<This> HttpApiAlg for This where
126 This: HandlerAlg + HttpInputAlg + HttpRouteAlg + RouteAlg<Endpoint = <This as HandlerAlg>::Endpoint>
127{
128}
129
130pub struct Routes<'a, Alg>
132where
133 Alg: RouteAlg,
134{
135 alg: &'a Alg,
136 route: Alg::Route,
137}
138
139impl<Alg> Routes<'_, Alg>
140where
141 Alg: RouteAlg,
142{
143 pub fn into_route(self) -> Alg::Route {
145 self.route
146 }
147
148 #[must_use]
150 pub fn coproduct(self, other: Self) -> Self {
151 let route = self.alg.coproduct(self.route, other.route);
152 Self { alg: self.alg, route }
153 }
154
155 #[must_use]
157 pub fn merge(self, other: Self) -> Self {
158 self.coproduct(other)
159 }
160}
161
162impl<Alg> Routes<'_, Alg>
163where
164 Alg: HttpRouteAlg,
165{
166 fn append(self, route: Alg::Route) -> Self {
167 let route = self.alg.coproduct(self.route, route);
168 Self { alg: self.alg, route }
169 }
170
171 #[must_use]
173 pub fn at(self, path: &str, endpoint: Alg::Endpoint) -> Self {
174 let route = self.alg.precompose(self.alg.http_path(path), self.alg.lift(endpoint));
175 self.append(route)
176 }
177
178 #[must_use]
180 pub fn nest(self, prefix: &str, nested: Self) -> Self {
181 let route = self.alg.precompose(self.alg.http_prefix(prefix), nested.route);
182 self.append(route)
183 }
184}
185
186#[ext(name = RouteAlgExt, supertraits = RouteAlg + Sized)]
188pub impl<This> This
189where
190 This: RouteAlg,
191{
192 fn routes(&self) -> Routes<'_, Self> {
194 Routes { alg: self, route: self.initial() }
195 }
196
197 fn route(&self, route: This::Route) -> Routes<'_, Self> {
199 Routes { alg: self, route }
200 }
201}
202
203#[ext(name = HttpProgramExt)]
205pub impl<This> This {
206 fn compile_http<Program>(&self, program: Program) -> Program::Route
208 where
209 Program: HttpProgramAlg<This>,
210 {
211 program.compile_http(self)
212 }
213}