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