1use crate::error::Result;
4use crate::types::{HttpMethod, Request, Response};
5use async_trait::async_trait;
6use std::future::Future;
7use std::pin::Pin;
8
9pub type BoxFuture<T> = Pin<Box<dyn Future<Output = T> + Send + 'static>>;
11
12pub type HandlerFn = Box<dyn Fn(Request) -> BoxFuture<Result<Response>> + Send + Sync>;
14
15#[async_trait]
17pub trait Middleware: Send + Sync {
18 async fn call(&self, req: Request, next: Next) -> Result<Response>;
20}
21
22pub struct Next {
24 handler: Box<dyn Fn(Request) -> BoxFuture<Result<Response>> + Send + Sync>,
25}
26
27impl Next {
28 pub fn new(handler: HandlerFn) -> Self {
29 Self { handler }
30 }
31
32 pub async fn run(self, req: Request) -> Result<Response> {
33 (self.handler)(req).await
34 }
35}
36
37pub trait Handler<T>: Clone + Send + Sync + 'static {
39 fn into_handler(self) -> HandlerFn;
40}
41
42impl<F, Fut> Handler<()> for F
44where
45 F: Fn(Request) -> Fut + Clone + Send + Sync + 'static,
46 Fut: Future<Output = Result<Response>> + Send + 'static,
47{
48 fn into_handler(self) -> HandlerFn {
49 Box::new(move |req| {
50 let handler = self.clone();
51 Box::pin(async move { handler(req).await })
52 })
53 }
54}
55
56pub struct Route {
58 pub path: String,
59 pub method: HttpMethod,
60 pub handler: HandlerFn,
61 pub is_dynamic: bool,
63}
64
65impl Route {
66 pub fn new<H, T>(path: impl Into<String>, method: HttpMethod, handler: H) -> Self
67 where
68 H: Handler<T>,
69 {
70 let path_str = path.into();
71 let is_dynamic = path_str.contains(':') || path_str.contains('*');
72
73 Self {
74 path: path_str,
75 method,
76 handler: handler.into_handler(),
77 is_dynamic,
78 }
79 }
80
81 pub fn matches(&self, path: &str) -> bool {
83 if !self.is_dynamic {
84 return self.path == path;
85 }
86
87 self.match_dynamic_path(path).is_some()
88 }
89
90 pub fn extract_params(&self, path: &str) -> std::collections::HashMap<String, String> {
92 self.match_dynamic_path(path).unwrap_or_default()
93 }
94
95 fn match_dynamic_path(&self, path: &str) -> Option<std::collections::HashMap<String, String>> {
97 let route_parts: Vec<&str> = self.path.split('/').collect();
98 let path_parts: Vec<&str> = path.split('/').collect();
99
100 if route_parts.len() != path_parts.len() {
101 if let Some(last_part) = route_parts.last()
103 && last_part.starts_with('*')
104 && route_parts.len() <= path_parts.len()
105 {
106 let mut params = std::collections::HashMap::new();
108 let param_name = last_part.trim_start_matches('*');
109 if !param_name.is_empty() {
110 let remaining_path = path_parts[route_parts.len() - 1..].join("/");
111 params.insert(param_name.to_string(), remaining_path);
112 }
113 return Some(params);
114 }
115 return None;
116 }
117
118 let mut params = std::collections::HashMap::new();
119
120 for (route_part, path_part) in route_parts.iter().zip(path_parts.iter()) {
121 if route_part.starts_with(':') {
122 let param_name = route_part.trim_start_matches(':');
124 params.insert(param_name.to_string(), path_part.to_string());
125 } else if route_part.starts_with('*') {
126 let param_name = route_part.trim_start_matches('*');
128 if !param_name.is_empty() {
129 params.insert(param_name.to_string(), path_part.to_string());
130 }
131 } else if route_part != path_part {
132 return None;
134 }
135 }
136
137 Some(params)
138 }
139}
140
141pub enum AdapterType {
143 Mock(crate::adapters::mock::MockAdapter),
144 #[cfg(feature = "axum")]
145 Axum(crate::adapters::axum::AxumAdapter),
146 #[cfg(feature = "actix-web")]
147 ActixWeb(crate::adapters::actix_web::ActixWebAdapter),
148 #[cfg(feature = "warp")]
149 Warp(crate::adapters::warp::WarpAdapter),
150 #[cfg(feature = "rocket")]
151 Rocket(crate::adapters::rocket::RocketAdapter),
152 #[cfg(feature = "salvo")]
153 Salvo(crate::adapters::salvo::SalvoAdapter),
154 #[cfg(feature = "poem")]
155 Poem(crate::adapters::poem::PoemAdapter),
156}
157
158pub struct WebServer {
160 adapter: AdapterType,
161 routes: Vec<Route>,
162 middleware: Vec<Box<dyn Middleware>>,
163}
164
165impl Default for WebServer {
166 fn default() -> Self {
167 Self::new()
168 }
169}
170
171impl WebServer {
172 pub fn new() -> Self {
174 Self::with_mock_adapter()
175 }
176
177 pub fn with_mock_adapter() -> Self {
179 Self {
180 adapter: AdapterType::Mock(crate::adapters::mock::MockAdapter::new()),
181 routes: Vec::new(),
182 middleware: Vec::new(),
183 }
184 }
185
186 #[cfg(feature = "axum")]
188 pub fn with_axum_adapter() -> Self {
189 Self {
190 adapter: AdapterType::Axum(crate::adapters::axum::AxumAdapter::new()),
191 routes: Vec::new(),
192 middleware: Vec::new(),
193 }
194 }
195
196 #[cfg(feature = "actix-web")]
198 pub fn with_actix_web_adapter() -> Self {
199 Self {
200 adapter: AdapterType::ActixWeb(crate::adapters::actix_web::ActixWebAdapter::new()),
201 routes: Vec::new(),
202 middleware: Vec::new(),
203 }
204 }
205
206 #[cfg(feature = "warp")]
208 pub fn with_warp_adapter() -> Self {
209 Self {
210 adapter: AdapterType::Warp(crate::adapters::warp::WarpAdapter::new()),
211 routes: Vec::new(),
212 middleware: Vec::new(),
213 }
214 }
215
216 #[cfg(feature = "rocket")]
220 pub fn with_rocket_adapter() -> Self {
221 Self {
222 adapter: AdapterType::Rocket(crate::adapters::rocket::RocketAdapter::new()),
223 routes: Vec::new(),
224 middleware: Vec::new(),
225 }
226 }
227
228 #[cfg(feature = "salvo")]
230 pub fn with_salvo_adapter() -> Self {
231 Self {
232 adapter: AdapterType::Salvo(crate::adapters::salvo::SalvoAdapter::new()),
233 routes: Vec::new(),
234 middleware: Vec::new(),
235 }
236 }
237
238 #[cfg(feature = "poem")]
240 pub fn with_poem_adapter() -> Self {
241 Self {
242 adapter: AdapterType::Poem(crate::adapters::poem::PoemAdapter::new()),
243 routes: Vec::new(),
244 middleware: Vec::new(),
245 }
246 }
247
248 pub fn route<H, T>(mut self, path: impl Into<String>, method: HttpMethod, handler: H) -> Self
250 where
251 H: Handler<T>,
252 {
253 let route = Route::new(path, method, handler);
254 self.routes.push(route);
255 self
256 }
257
258 pub fn middleware<M>(mut self, middleware: M) -> Self
260 where
261 M: Middleware + 'static,
262 {
263 self.middleware.push(Box::new(middleware));
264 self
265 }
266
267 pub fn with_path_params(mut self) -> Self {
271 let route_patterns: Vec<(String, HttpMethod)> = self
273 .routes
274 .iter()
275 .map(|r| (r.path.clone(), r.method))
276 .collect();
277
278 let param_middleware = crate::middleware::PathParameterMiddleware::new(route_patterns);
279 self.middleware.push(Box::new(param_middleware));
280 self
281 }
282
283 pub fn websocket(mut self, path: impl Into<String>) -> Self {
287 let websocket_handler = |req: crate::types::Request| async move {
289 if req
291 .headers
292 .get("Upgrade")
293 .is_some_and(|v| v.to_lowercase() == "websocket")
294 {
295 match crate::types::WebSocketUpgrade::from_request(req) {
297 Ok(upgrade) => {
298 let accept_key = upgrade.generate_accept_key();
299 Ok(crate::types::Response::new(
300 crate::types::StatusCode::SWITCHING_PROTOCOLS,
301 )
302 .header("Upgrade", "websocket")
303 .header("Connection", "Upgrade")
304 .header("Sec-WebSocket-Accept", accept_key))
305 }
306 Err(e) => Err(e),
307 }
308 } else {
309 Err(crate::error::WebServerError::custom(
310 "Not a WebSocket upgrade request",
311 ))
312 }
313 };
314
315 self.routes.push(Route::new(
316 path,
317 crate::types::HttpMethod::GET,
318 websocket_handler,
319 ));
320 self
321 }
322
323 pub fn get<H, T>(self, path: impl Into<String>, handler: H) -> Self
325 where
326 H: Handler<T>,
327 {
328 self.route(path, crate::types::HttpMethod::GET, handler)
329 }
330
331 pub fn post<H, T>(self, path: impl Into<String>, handler: H) -> Self
333 where
334 H: Handler<T>,
335 {
336 self.route(path, crate::types::HttpMethod::POST, handler)
337 }
338
339 pub fn put<H, T>(self, path: impl Into<String>, handler: H) -> Self
341 where
342 H: Handler<T>,
343 {
344 self.route(path, crate::types::HttpMethod::PUT, handler)
345 }
346
347 pub fn delete<H, T>(self, path: impl Into<String>, handler: H) -> Self
349 where
350 H: Handler<T>,
351 {
352 self.route(path, crate::types::HttpMethod::DELETE, handler)
353 }
354
355 pub fn patch<H, T>(self, path: impl Into<String>, handler: H) -> Self
357 where
358 H: Handler<T>,
359 {
360 self.route(path, crate::types::HttpMethod::PATCH, handler)
361 }
362
363 pub fn head<H, T>(self, path: impl Into<String>, handler: H) -> Self
365 where
366 H: Handler<T>,
367 {
368 self.route(path, crate::types::HttpMethod::HEAD, handler)
369 }
370
371 pub fn options<H, T>(self, path: impl Into<String>, handler: H) -> Self
373 where
374 H: Handler<T>,
375 {
376 self.route(path, crate::types::HttpMethod::OPTIONS, handler)
377 }
378
379 pub fn trace<H, T>(self, path: impl Into<String>, handler: H) -> Self
381 where
382 H: Handler<T>,
383 {
384 self.route(path, crate::types::HttpMethod::TRACE, handler)
385 }
386
387 pub fn connect<H, T>(self, path: impl Into<String>, handler: H) -> Self
389 where
390 H: Handler<T>,
391 {
392 self.route(path, crate::types::HttpMethod::CONNECT, handler)
393 }
394
395 pub fn param_route<H, T>(self, path: impl Into<String>, method: HttpMethod, handler: H) -> Self
397 where
398 H: Handler<T>,
399 {
400 self.route(path, method, handler)
402 }
403
404 pub fn wildcard_route<H, T>(
406 self,
407 path: impl Into<String>,
408 method: HttpMethod,
409 handler: H,
410 ) -> Self
411 where
412 H: Handler<T>,
413 {
414 self.route(path, method, handler)
416 }
417
418 pub async fn bind(mut self, addr: &str) -> Result<BoundServer> {
420 match &mut self.adapter {
422 AdapterType::Mock(adapter) => {
423 for route in self.routes {
424 adapter.route(&route.path, route.method, route.handler);
425 }
426 for middleware in self.middleware {
427 adapter.middleware(middleware);
428 }
429 adapter.bind(addr).await?;
430 }
431 #[cfg(feature = "axum")]
432 AdapterType::Axum(adapter) => {
433 for route in self.routes {
434 adapter.route(&route.path, route.method, route.handler);
435 }
436 for middleware in self.middleware {
437 adapter.middleware(middleware);
438 }
439 adapter.bind(addr).await?;
440 }
441 #[cfg(feature = "actix-web")]
442 AdapterType::ActixWeb(adapter) => {
443 for route in self.routes {
444 adapter.route(&route.path, route.method, route.handler);
445 }
446 for middleware in self.middleware {
447 adapter.middleware(middleware);
448 }
449 adapter.bind(addr).await?;
450 }
451 #[cfg(feature = "warp")]
452 AdapterType::Warp(adapter) => {
453 for route in self.routes {
454 adapter.route(&route.path, route.method, route.handler);
455 }
456 for middleware in self.middleware {
457 adapter.middleware(middleware);
458 }
459 adapter.bind(addr).await?;
460 }
461 #[cfg(feature = "rocket")]
462 AdapterType::Rocket(adapter) => {
463 for route in self.routes {
464 adapter.route(&route.path, route.method, route.handler);
465 }
466 for middleware in self.middleware {
467 adapter.middleware(middleware);
468 }
469 adapter.bind(addr).await?;
470 }
471 #[cfg(feature = "salvo")]
472 AdapterType::Salvo(adapter) => {
473 for route in self.routes {
474 adapter.route(&route.path, route.method, route.handler);
475 }
476 for middleware in self.middleware {
477 adapter.middleware(middleware);
478 }
479 adapter.bind(addr).await?;
480 }
481 #[cfg(feature = "poem")]
482 AdapterType::Poem(adapter) => {
483 for route in self.routes {
484 adapter.route(&route.path, route.method, route.handler);
485 }
486 for middleware in self.middleware {
487 adapter.middleware(middleware);
488 }
489 adapter.bind(addr).await?;
490 }
491 }
492
493 Ok(BoundServer {
494 adapter: self.adapter,
495 })
496 }
497}
498
499pub struct BoundServer {
501 adapter: AdapterType,
502}
503
504impl BoundServer {
505 pub async fn run(self) -> Result<()> {
507 match self.adapter {
508 AdapterType::Mock(adapter) => adapter.run().await,
509 #[cfg(feature = "axum")]
510 AdapterType::Axum(adapter) => adapter.run().await,
511 #[cfg(feature = "actix-web")]
512 AdapterType::ActixWeb(adapter) => adapter.run().await,
513 #[cfg(feature = "warp")]
514 AdapterType::Warp(adapter) => adapter.run().await,
515 #[cfg(feature = "rocket")]
516 AdapterType::Rocket(adapter) => adapter.run().await,
517 #[cfg(feature = "salvo")]
518 AdapterType::Salvo(adapter) => adapter.run().await,
519 #[cfg(feature = "poem")]
520 AdapterType::Poem(adapter) => adapter.run().await,
521 }
522 }
523}
524
525