alux_http_direct/
route.rs1use crate::{DirectError, DirectRequest, DirectResponse};
4use alux_http::{HttpMethod, HttpSelectorAlg, PathSegment, RouteAlg, RoutePath, SelectorAlg, describe_path};
5use core::future::Future;
6use core::pin::Pin;
7use std::sync::Arc;
8
9pub type Answer = Pin<Box<dyn Future<Output = DirectResponse> + Send>>;
11
12type Reached = Arc<dyn Fn(DirectRequest, Vec<String>) -> Answer + Send + Sync>;
13
14#[derive(Debug, Clone, PartialEq, Eq)]
15enum DirectSelectorPart {
16 Method(HttpMethod),
17 Path(RoutePath),
18 Prefix(RoutePath),
19}
20
21#[derive(Debug, Clone, Default, PartialEq, Eq)]
23pub struct DirectSelector {
24 parts: Vec<DirectSelectorPart>,
25}
26
27impl DirectSelector {
28 pub fn path(&self) -> String {
32 describe_path(self.paths())
33 }
34
35 pub fn label(&self) -> String {
37 let method = self.method().map_or("*", HttpMethod::label);
38 format!("{method} {}", self.path())
39 }
40
41 fn paths(&self) -> impl Iterator<Item = &RoutePath> {
42 self.parts.iter().filter_map(|part| match part {
43 DirectSelectorPart::Path(path) | DirectSelectorPart::Prefix(path) => Some(path),
44 DirectSelectorPart::Method(_) => None,
45 })
46 }
47
48 fn segments(&self) -> Vec<&PathSegment> {
49 self.paths().flat_map(RoutePath::segments).collect()
50 }
51
52 fn method(&self) -> Option<HttpMethod> {
53 self.parts.iter().rev().find_map(|part| match part {
54 DirectSelectorPart::Method(method) => Some(*method),
55 DirectSelectorPart::Path(_) | DirectSelectorPart::Prefix(_) => None,
56 })
57 }
58
59 fn captures(&self, path: &str) -> Option<Vec<String>> {
64 let mut asked = path.split('/').filter(|segment| !segment.is_empty()).peekable();
65 let mut captured = Vec::new();
66 for segment in self.segments() {
67 match segment {
68 PathSegment::Literal(value) => {
69 if asked.next()? != value {
70 return None;
71 }
72 }
73 PathSegment::Param(_) => captured.push(asked.next()?.to_owned()),
74 PathSegment::Tail(_) => {
75 captured.push(asked.by_ref().collect::<Vec<_>>().join("/"));
76
77 return Some(captured);
78 }
79 }
80 }
81
82 asked.next().is_none().then_some(captured)
83 }
84}
85
86#[derive(Clone)]
88pub struct DirectEndpoint(Reached);
89
90impl DirectEndpoint {
91 pub fn new<Reach>(reach: Reach) -> Self
93 where
94 Reach: Fn(DirectRequest, Vec<String>) -> Answer + Send + Sync + 'static,
95 {
96 Self(Arc::new(reach))
97 }
98}
99
100#[derive(Clone)]
101struct DirectRouteEntry {
102 selector: DirectSelector,
103 endpoint: DirectEndpoint,
104}
105
106#[derive(Clone, Default)]
108pub struct DirectRoute {
109 entries: Vec<DirectRouteEntry>,
110}
111
112impl DirectRoute {
113 pub fn labels(&self) -> Vec<String> {
115 self.entries.iter().map(|entry| entry.selector.label()).collect()
116 }
117
118 pub fn paths(&self) -> Vec<String> {
120 self.entries.iter().map(|entry| entry.selector.path()).collect()
121 }
122
123 pub async fn answer(&self, request: DirectRequest) -> DirectResponse {
129 let mut allowed = false;
130 for entry in &self.entries {
131 let Some(captures) = entry.selector.captures(request.path()) else { continue };
132 allowed = true;
133 if entry.selector.method().is_none_or(|method| Some(method) == request.method()) {
134 return (entry.endpoint.0)(request, captures).await;
135 }
136 }
137
138 let error = if allowed {
139 DirectError::method_not_allowed(request.path())
140 } else {
141 DirectError::not_found(request.path())
142 };
143
144 error.into()
145 }
146}
147
148#[derive(Debug, Default)]
150pub struct DirectRouteImpl;
151
152impl SelectorAlg for DirectRouteImpl {
153 type Selector = DirectSelector;
154
155 fn identity(&self) -> DirectSelector {
156 DirectSelector::default()
157 }
158
159 fn compose(&self, mut first: DirectSelector, second: DirectSelector) -> DirectSelector {
160 first.parts.extend(second.parts);
161 first
162 }
163}
164
165impl RouteAlg for DirectRouteImpl {
166 type Route = DirectRoute;
167 type Selector = DirectSelector;
168 type Endpoint = DirectEndpoint;
169
170 fn initial(&self) -> DirectRoute {
171 DirectRoute::default()
172 }
173
174 fn coproduct(&self, mut left: DirectRoute, right: DirectRoute) -> DirectRoute {
175 left.entries.extend(right.entries);
176 left
177 }
178
179 fn precompose(&self, selector: DirectSelector, mut route: DirectRoute) -> DirectRoute {
180 for entry in &mut route.entries {
181 entry.selector = self.compose(selector.clone(), core::mem::take(&mut entry.selector));
182 }
183 route
184 }
185
186 fn lift(&self, endpoint: DirectEndpoint) -> DirectRoute {
187 DirectRoute { entries: vec![DirectRouteEntry { selector: self.identity(), endpoint }] }
188 }
189}
190
191impl HttpSelectorAlg for DirectRouteImpl {
192 type Selector = DirectSelector;
193
194 fn http_method(&self, method: HttpMethod) -> DirectSelector {
195 DirectSelector { parts: vec![DirectSelectorPart::Method(method)] }
196 }
197
198 fn http_path(&self, path: &RoutePath) -> DirectSelector {
199 DirectSelector { parts: vec![DirectSelectorPart::Path(path.clone())] }
200 }
201
202 fn http_prefix(&self, prefix: &RoutePath) -> DirectSelector {
203 DirectSelector { parts: vec![DirectSelectorPart::Prefix(prefix.clone())] }
204 }
205}