1use crate::WarpRequest;
4use alux_http::{HttpMethod, HttpSelectorAlg, PathSegment, RouteAlg, RoutePath, SelectorAlg, describe_path};
5use bytes::Bytes;
6use core::future::Future;
7use core::pin::Pin;
8use std::sync::Arc;
9use warp::Filter;
10use warp::filters::BoxedFilter;
11use warp::http::Method;
12use warp::reply::Response;
13
14pub type Answer = Pin<Box<dyn Future<Output = Response> + Send>>;
16
17fn warp_method(method: HttpMethod) -> Method {
19 match method {
20 HttpMethod::Get => Method::GET,
21 HttpMethod::Post => Method::POST,
22 HttpMethod::Put => Method::PUT,
23 HttpMethod::Patch => Method::PATCH,
24 HttpMethod::Delete => Method::DELETE,
25 HttpMethod::Head => Method::HEAD,
26 HttpMethod::Options => Method::OPTIONS,
27 HttpMethod::Trace => Method::TRACE,
28 HttpMethod::Connect => Method::CONNECT,
29 }
30}
31
32#[derive(Debug, Clone, PartialEq, Eq)]
33enum WarpSelectorPart {
34 Method(HttpMethod),
35 Path(RoutePath),
36 Prefix(RoutePath),
37}
38
39#[derive(Debug, Clone, Default, PartialEq, Eq)]
41pub struct WarpSelector {
42 parts: Vec<WarpSelectorPart>,
43}
44
45impl WarpSelector {
46 pub fn path(&self) -> String {
51 describe_path(self.paths())
52 }
53
54 pub fn label(&self) -> String {
56 let method = self.method().map_or("*", HttpMethod::label);
57 format!("{method} {}", self.path())
58 }
59
60 fn paths(&self) -> impl Iterator<Item = &RoutePath> {
61 self.parts.iter().filter_map(|part| match part {
62 WarpSelectorPart::Path(path) | WarpSelectorPart::Prefix(path) => Some(path),
63 WarpSelectorPart::Method(_) => None,
64 })
65 }
66
67 fn segments(&self) -> Vec<PathSegment> {
68 self.paths().flat_map(|path| path.segments().iter().cloned()).collect()
69 }
70
71 fn method(&self) -> Option<HttpMethod> {
72 self.parts.iter().rev().find_map(|part| match part {
73 WarpSelectorPart::Method(method) => Some(*method),
74 WarpSelectorPart::Path(_) | WarpSelectorPart::Prefix(_) => None,
75 })
76 }
77
78 fn filter(&self) -> BoxedFilter<(Vec<String>,)> {
80 let segments = self.segments();
81 let mut bound = warp::any().map(Vec::<String>::new).boxed();
82 let mut open = false;
83 for segment in &segments {
84 bound = match segment {
85 PathSegment::Literal(value) => bound.and(warp::path(value.clone())).boxed(),
86 PathSegment::Param(_) => bound
87 .and(warp::path::param::<String>())
88 .map(|mut bound: Vec<String>, value: String| {
89 bound.push(value);
90 bound
91 })
92 .boxed(),
93 PathSegment::Tail(_) => {
94 open = true;
95 bound
96 .and(warp::path::tail())
97 .map(|mut bound: Vec<String>, tail: warp::path::Tail| {
98 bound.push(tail.as_str().to_owned());
99 bound
100 })
101 .boxed()
102 }
103 };
104 }
105
106 let bound = if open { bound } else { bound.and(warp::path::end()).boxed() };
108
109 match self.method() {
110 Some(method) => {
111 let expected = warp_method(method);
112 bound
113 .and(warp::method())
114 .and_then(move |bound: Vec<String>, method: Method| {
115 let expected = expected.clone();
116 async move { if method == expected { Ok(bound) } else { Err(warp::reject()) } }
117 })
118 .boxed()
119 }
120 None => bound,
121 }
122 }
123}
124
125#[derive(Clone)]
127pub struct WarpEndpoint(Arc<dyn Fn(WarpRequest) -> Answer + Send + Sync>);
128
129impl WarpEndpoint {
130 pub fn new<Reach>(reach: Reach) -> Self
132 where
133 Reach: Fn(WarpRequest) -> Answer + Send + Sync + 'static,
134 {
135 Self(Arc::new(reach))
136 }
137}
138
139#[derive(Clone)]
140struct WarpRouteEntry {
141 selector: WarpSelector,
142 endpoint: WarpEndpoint,
143}
144
145#[derive(Clone, Default)]
147pub struct WarpRoute {
148 entries: Vec<WarpRouteEntry>,
149}
150
151impl WarpRoute {
152 pub fn labels(&self) -> Vec<String> {
154 self.entries.iter().map(|entry| entry.selector.label()).collect()
155 }
156
157 pub fn paths(&self) -> Vec<String> {
159 self.entries.iter().map(|entry| entry.selector.path()).collect()
160 }
161
162 pub fn into_warp(self) -> BoxedFilter<(Response,)> {
166 let nothing = warp::any().and_then(|| async { Err::<Response, _>(warp::reject::not_found()) }).boxed();
167
168 self.entries.into_iter().fold(nothing, |answered, entry| {
169 let endpoint = entry.endpoint.clone();
170 let reached = entry
171 .selector
172 .filter()
173 .and(warp::query::raw().or(warp::any().map(String::new)).unify())
174 .and(warp::header::headers_cloned())
175 .and(warp::body::bytes())
176 .and_then(move |captures, query, headers, body: Bytes| {
177 let endpoint = endpoint.clone();
178 async move {
179 let request = WarpRequest { captures, query, headers, body: body.to_vec() };
180
181 Ok::<_, warp::Rejection>((endpoint.0)(request).await)
182 }
183 })
184 .boxed();
185
186 answered.or(reached).unify().boxed()
187 })
188 }
189}
190
191#[derive(Debug, Default)]
193pub struct WarpRouteImpl;
194
195impl SelectorAlg for WarpRouteImpl {
196 type Selector = WarpSelector;
197
198 fn identity(&self) -> WarpSelector {
199 WarpSelector::default()
200 }
201
202 fn compose(&self, mut first: WarpSelector, second: WarpSelector) -> WarpSelector {
203 first.parts.extend(second.parts);
204 first
205 }
206}
207
208impl RouteAlg for WarpRouteImpl {
209 type Route = WarpRoute;
210 type Selector = WarpSelector;
211 type Endpoint = WarpEndpoint;
212
213 fn initial(&self) -> WarpRoute {
214 WarpRoute::default()
215 }
216
217 fn coproduct(&self, mut left: WarpRoute, right: WarpRoute) -> WarpRoute {
218 left.entries.extend(right.entries);
219 left
220 }
221
222 fn precompose(&self, selector: WarpSelector, mut route: WarpRoute) -> WarpRoute {
223 for entry in &mut route.entries {
224 entry.selector = self.compose(selector.clone(), core::mem::take(&mut entry.selector));
225 }
226 route
227 }
228
229 fn lift(&self, endpoint: WarpEndpoint) -> WarpRoute {
230 WarpRoute { entries: vec![WarpRouteEntry { selector: self.identity(), endpoint }] }
231 }
232}
233
234impl HttpSelectorAlg for WarpRouteImpl {
235 type Selector = WarpSelector;
236
237 fn http_method(&self, method: HttpMethod) -> WarpSelector {
238 WarpSelector { parts: vec![WarpSelectorPart::Method(method)] }
239 }
240
241 fn http_path(&self, path: &RoutePath) -> WarpSelector {
242 WarpSelector { parts: vec![WarpSelectorPart::Path(path.clone())] }
243 }
244
245 fn http_prefix(&self, prefix: &RoutePath) -> WarpSelector {
246 WarpSelector { parts: vec![WarpSelectorPart::Prefix(prefix.clone())] }
247 }
248}