1use crate::middleware::v2::{MiddlewarePipelineV2, NextFuture};
11use crate::request::ElifRequest;
12use crate::response::{ElifResponse, ElifStatusCode};
13use crate::routing::{HttpMethod, RouteMatch, RouteMatcher};
14use std::collections::HashMap;
15use std::sync::Arc;
16use thiserror::Error;
17
18#[derive(Error, Debug)]
20pub enum PipelineError {
21 #[error("Route not found: {method} {path}")]
22 RouteNotFound { method: HttpMethod, path: String },
23
24 #[error("Method not allowed: {method}")]
25 MethodNotAllowed { method: String },
26
27 #[error("Parameter error: {0}")]
28 Parameter(#[from] ParamError),
29
30 #[error("Middleware error: {0}")]
31 Middleware(String),
32
33 #[error("Handler error: {0}")]
34 Handler(String),
35
36 #[error("Internal pipeline error: {0}")]
37 Internal(String),
38}
39
40#[derive(Error, Debug)]
42pub enum ParamError {
43 #[error("Missing parameter: {0}")]
44 Missing(String),
45
46 #[error("Failed to parse parameter '{param}' with value '{value}': {error}")]
47 ParseError {
48 param: String,
49 value: String,
50 error: String,
51 },
52}
53
54pub type HandlerFn = dyn Fn(ElifRequest) -> NextFuture<'static> + Send + Sync;
56
57#[derive(Debug, Clone)]
59pub struct MiddlewareGroup {
60 pub name: String,
61 pub pipeline: MiddlewarePipelineV2,
62}
63
64pub struct RequestPipeline {
66 matcher: Arc<RouteMatcher>,
68 global_middleware: MiddlewarePipelineV2,
70 middleware_groups: HashMap<String, MiddlewarePipelineV2>,
72 handlers: Arc<HashMap<String, Arc<HandlerFn>>>,
74}
75
76impl RequestPipeline {
77 pub fn new(matcher: RouteMatcher) -> Self {
79 Self {
80 matcher: Arc::new(matcher),
81 global_middleware: MiddlewarePipelineV2::new(),
82 middleware_groups: HashMap::new(),
83 handlers: Arc::new(HashMap::new()),
84 }
85 }
86
87 pub fn add_global_middleware<M>(mut self, middleware: M) -> Self
89 where
90 M: crate::middleware::v2::Middleware + 'static,
91 {
92 self.global_middleware = self.global_middleware.add(middleware);
93 self
94 }
95
96 pub fn add_middleware_group<S: Into<String>>(
98 mut self,
99 name: S,
100 pipeline: MiddlewarePipelineV2,
101 ) -> Self {
102 self.middleware_groups.insert(name.into(), pipeline);
103 self
104 }
105
106 pub fn add_handler<S: Into<String>, F>(mut self, route_id: S, handler: F) -> Self
108 where
109 F: Fn(ElifRequest) -> NextFuture<'static> + Send + Sync + 'static,
110 {
111 let handlers = Arc::get_mut(&mut self.handlers)
114 .expect("RequestPipeline handlers should be exclusively owned during building");
115 handlers.insert(route_id.into(), Arc::new(handler));
116 self
117 }
118
119 pub async fn process(&self, request: ElifRequest) -> ElifResponse {
121 match self.process_internal(request).await {
122 Ok(response) => response,
123 Err(error) => self.handle_pipeline_error(error),
124 }
125 }
126
127 async fn process_internal(&self, request: ElifRequest) -> Result<ElifResponse, PipelineError> {
129 let route_match = self.resolve_route(&request)?;
131
132 let request_with_params = self.inject_params(request, &route_match);
134
135 let complete_pipeline = self.build_route_pipeline(&route_match)?;
137
138 let route_id = route_match.route_id.clone();
141 let handlers = Arc::clone(&self.handlers);
142 let response = complete_pipeline.execute(request_with_params, move |req| {
143 let route_id = route_id.clone();
144 let handlers = Arc::clone(&handlers);
145 Box::pin(async move {
146 match handlers.get(&route_id) {
147 Some(handler) => handler(req).await,
148 None => {
149 ElifResponse::internal_server_error()
150 .with_json(&serde_json::json!({
151 "error": {
152 "code": "handler_not_found",
153 "message": format!("No handler registered for route: {}", route_id)
154 }
155 }))
156 }
157 }
158 })
159 }).await;
160
161 Ok(response)
162 }
163
164 fn resolve_route(&self, request: &ElifRequest) -> Result<RouteMatch, PipelineError> {
166 let http_method = match request.method.to_axum() {
167 &axum::http::Method::GET => HttpMethod::GET,
168 &axum::http::Method::POST => HttpMethod::POST,
169 &axum::http::Method::PUT => HttpMethod::PUT,
170 &axum::http::Method::DELETE => HttpMethod::DELETE,
171 &axum::http::Method::PATCH => HttpMethod::PATCH,
172 &axum::http::Method::HEAD => HttpMethod::HEAD,
173 &axum::http::Method::OPTIONS => HttpMethod::OPTIONS,
174 &axum::http::Method::TRACE => HttpMethod::TRACE,
175 unsupported => {
176 return Err(PipelineError::MethodNotAllowed {
177 method: unsupported.to_string(),
178 });
179 }
180 };
181
182 self.matcher
183 .resolve(&http_method, request.path())
184 .ok_or_else(|| PipelineError::RouteNotFound {
185 method: http_method,
186 path: request.path().to_string(),
187 })
188 }
189
190 fn inject_params(&self, mut request: ElifRequest, route_match: &RouteMatch) -> ElifRequest {
192 for (key, value) in &route_match.params {
193 request.add_path_param(key, value);
194 }
195 request
196 }
197
198 fn build_route_pipeline(
200 &self,
201 _route_match: &RouteMatch,
202 ) -> Result<MiddlewarePipelineV2, PipelineError> {
203 let pipeline = self.global_middleware.clone();
204
205 Ok(pipeline)
210 }
211
212 fn handle_pipeline_error(&self, error: PipelineError) -> ElifResponse {
214 match error {
215 PipelineError::RouteNotFound { .. } => {
216 ElifResponse::not_found().with_json(&serde_json::json!({
217 "error": {
218 "code": "not_found",
219 "message": "The requested resource was not found"
220 }
221 }))
222 }
223 PipelineError::MethodNotAllowed { method } => ElifResponse::with_status(
224 ElifStatusCode::METHOD_NOT_ALLOWED,
225 )
226 .with_json(&serde_json::json!({
227 "error": {
228 "code": "method_not_allowed",
229 "message": format!("HTTP method '{}' is not supported", method),
230 "hint": "Supported methods: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, TRACE"
231 }
232 })),
233 PipelineError::Parameter(param_error) => {
234 ElifResponse::bad_request().with_json(&serde_json::json!({
235 "error": {
236 "code": "parameter_error",
237 "message": param_error.to_string()
238 }
239 }))
240 }
241 PipelineError::Middleware(msg)
242 | PipelineError::Handler(msg)
243 | PipelineError::Internal(msg) => {
244 ElifResponse::internal_server_error().with_json(&serde_json::json!({
245 "error": {
246 "code": "internal_error",
247 "message": msg
248 }
249 }))
250 }
251 }
252 }
253
254 pub fn stats(&self) -> PipelineStats {
256 PipelineStats {
257 total_routes: self.matcher.all_routes().len(),
258 global_middleware_count: self.global_middleware.len(),
259 middleware_groups: self.middleware_groups.len(),
260 registered_handlers: self.handlers.len(),
261 }
262 }
263
264 pub fn matcher(&self) -> &RouteMatcher {
266 &self.matcher
267 }
268
269 pub fn global_middleware(&self) -> &MiddlewarePipelineV2 {
271 &self.global_middleware
272 }
273
274 pub fn middleware_groups(&self) -> &HashMap<String, MiddlewarePipelineV2> {
276 &self.middleware_groups
277 }
278}
279
280impl std::fmt::Debug for RequestPipeline {
281 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
282 f.debug_struct("RequestPipeline")
283 .field("matcher", &self.matcher)
284 .field("global_middleware", &self.global_middleware)
285 .field("middleware_groups", &self.middleware_groups)
286 .field("handlers", &self.handlers.len())
287 .finish()
288 }
289}
290
291#[derive(Debug, Clone)]
293pub struct PipelineStats {
294 pub total_routes: usize,
295 pub global_middleware_count: usize,
296 pub middleware_groups: usize,
297 pub registered_handlers: usize,
298}
299
300pub struct RequestPipelineBuilder {
302 matcher: Option<RouteMatcher>,
303 global_middleware: MiddlewarePipelineV2,
304 middleware_groups: HashMap<String, MiddlewarePipelineV2>,
305 handlers: HashMap<String, Arc<HandlerFn>>,
306}
307
308impl RequestPipelineBuilder {
309 pub fn new() -> Self {
311 Self {
312 matcher: None,
313 global_middleware: MiddlewarePipelineV2::new(),
314 middleware_groups: HashMap::new(),
315 handlers: HashMap::new(),
316 }
317 }
318
319 pub fn matcher(mut self, matcher: RouteMatcher) -> Self {
321 self.matcher = Some(matcher);
322 self
323 }
324
325 pub fn global_middleware<M>(mut self, middleware: M) -> Self
327 where
328 M: crate::middleware::v2::Middleware + 'static,
329 {
330 self.global_middleware = self.global_middleware.add(middleware);
331 self
332 }
333
334 pub fn middleware_group<S: Into<String>>(
336 mut self,
337 name: S,
338 pipeline: MiddlewarePipelineV2,
339 ) -> Self {
340 self.middleware_groups.insert(name.into(), pipeline);
341 self
342 }
343
344 pub fn handler<S: Into<String>, F>(mut self, route_id: S, handler: F) -> Self
346 where
347 F: Fn(ElifRequest) -> NextFuture<'static> + Send + Sync + 'static,
348 {
349 self.handlers.insert(route_id.into(), Arc::new(handler));
350 self
351 }
352
353 pub fn build(self) -> Result<RequestPipeline, PipelineError> {
355 let matcher = self
356 .matcher
357 .ok_or_else(|| PipelineError::Internal("Route matcher is required".to_string()))?;
358
359 Ok(RequestPipeline {
360 matcher: Arc::new(matcher),
361 global_middleware: self.global_middleware,
362 middleware_groups: self.middleware_groups,
363 handlers: Arc::new(self.handlers),
364 })
365 }
366}
367
368impl Default for RequestPipelineBuilder {
369 fn default() -> Self {
370 Self::new()
371 }
372}
373
374pub mod parameter_extraction {
376 use super::{ElifRequest, ParamError};
377 use std::fmt::{Debug, Display};
378 use std::str::FromStr;
379
380 pub fn extract_path_param<T>(request: &ElifRequest, name: &str) -> Result<T, ParamError>
382 where
383 T: FromStr,
384 T::Err: Debug + Display,
385 {
386 let param_value = request
387 .path_param(name)
388 .ok_or_else(|| ParamError::Missing(name.to_string()))?;
389
390 param_value.parse().map_err(|e| ParamError::ParseError {
391 param: name.to_string(),
392 value: param_value.clone(),
393 error: format!("{}", e),
394 })
395 }
396
397 pub fn extract_query_param<T>(
399 request: &ElifRequest,
400 name: &str,
401 ) -> Result<Option<T>, ParamError>
402 where
403 T: FromStr,
404 T::Err: Debug + Display,
405 {
406 if let Some(param_value) = request.query_param(name) {
407 let parsed = param_value.parse().map_err(|e| ParamError::ParseError {
408 param: name.to_string(),
409 value: param_value.clone(),
410 error: format!("{}", e),
411 })?;
412 Ok(Some(parsed))
413 } else {
414 Ok(None)
415 }
416 }
417
418 pub fn extract_required_query_param<T>(
420 request: &ElifRequest,
421 name: &str,
422 ) -> Result<T, ParamError>
423 where
424 T: FromStr,
425 T::Err: Debug + Display,
426 {
427 extract_query_param(request, name)?.ok_or_else(|| ParamError::Missing(name.to_string()))
428 }
429}
430
431#[cfg(test)]
432mod tests {
433 use super::*;
434 use crate::middleware::v2::{LoggingMiddleware, MiddlewarePipelineV2};
435 use crate::response::{ElifResponse, ElifStatusCode};
436 use crate::routing::RouteMatcherBuilder;
437
438 #[tokio::test]
439 async fn test_basic_pipeline_processing() {
440 let matcher = RouteMatcherBuilder::new()
442 .get("home".to_string(), "/".to_string())
443 .get("user_show".to_string(), "/users/{id}".to_string())
444 .build()
445 .unwrap();
446
447 let pipeline = RequestPipelineBuilder::new()
449 .matcher(matcher)
450 .handler("home", |_req| {
451 Box::pin(async move { ElifResponse::ok().with_text("Welcome home!") })
452 })
453 .handler("user_show", |req| {
454 Box::pin(async move {
455 let user_id: u32 = match parameter_extraction::extract_path_param(&req, "id") {
456 Ok(id) => id,
457 Err(_) => return ElifResponse::bad_request().with_text("Invalid user ID"),
458 };
459 ElifResponse::ok().with_json(&serde_json::json!({
460 "user_id": user_id,
461 "message": "User details"
462 }))
463 })
464 })
465 .build()
466 .unwrap();
467
468 let home_request = ElifRequest::new(
470 crate::request::ElifMethod::GET,
471 "/".parse().unwrap(),
472 crate::response::ElifHeaderMap::new(),
473 );
474
475 let home_response = pipeline.process(home_request).await;
476 assert_eq!(home_response.status_code(), ElifStatusCode::OK);
477
478 let user_request = ElifRequest::new(
480 crate::request::ElifMethod::GET,
481 "/users/123".parse().unwrap(),
482 crate::response::ElifHeaderMap::new(),
483 );
484
485 let user_response = pipeline.process(user_request).await;
486 assert_eq!(user_response.status_code(), ElifStatusCode::OK);
487 }
488
489 #[tokio::test]
490 async fn test_pipeline_with_middleware() {
491 let matcher = RouteMatcherBuilder::new()
492 .get("test".to_string(), "/test".to_string())
493 .build()
494 .unwrap();
495
496 let pipeline = RequestPipelineBuilder::new()
497 .matcher(matcher)
498 .global_middleware(LoggingMiddleware)
499 .handler("test", |_req| {
500 Box::pin(async move { ElifResponse::ok().with_text("Test response") })
501 })
502 .build()
503 .unwrap();
504
505 let request = ElifRequest::new(
506 crate::request::ElifMethod::GET,
507 "/test".parse().unwrap(),
508 crate::response::ElifHeaderMap::new(),
509 );
510
511 let response = pipeline.process(request).await;
512 assert_eq!(response.status_code(), ElifStatusCode::OK);
513 }
514
515 #[tokio::test]
516 async fn test_pipeline_route_not_found() {
517 let matcher = RouteMatcherBuilder::new()
518 .get("home".to_string(), "/".to_string())
519 .build()
520 .unwrap();
521
522 let pipeline = RequestPipelineBuilder::new()
523 .matcher(matcher)
524 .build()
525 .unwrap();
526
527 let request = ElifRequest::new(
528 crate::request::ElifMethod::GET,
529 "/nonexistent".parse().unwrap(),
530 crate::response::ElifHeaderMap::new(),
531 );
532
533 let response = pipeline.process(request).await;
534 assert_eq!(response.status_code(), ElifStatusCode::NOT_FOUND);
535 }
536
537 #[tokio::test]
538 async fn test_pipeline_handler_not_found() {
539 let matcher = RouteMatcherBuilder::new()
540 .get("test".to_string(), "/test".to_string())
541 .build()
542 .unwrap();
543
544 let pipeline = RequestPipelineBuilder::new()
546 .matcher(matcher)
547 .build()
548 .unwrap();
549
550 let request = ElifRequest::new(
551 crate::request::ElifMethod::GET,
552 "/test".parse().unwrap(),
553 crate::response::ElifHeaderMap::new(),
554 );
555
556 let response = pipeline.process(request).await;
557 assert_eq!(
558 response.status_code(),
559 ElifStatusCode::INTERNAL_SERVER_ERROR
560 );
561 }
562
563 #[tokio::test]
564 async fn test_parameter_extraction_helpers() {
565 let mut request = ElifRequest::new(
566 crate::request::ElifMethod::GET,
567 "/users/123?page=2&limit=10".parse().unwrap(),
568 crate::response::ElifHeaderMap::new(),
569 );
570
571 request.add_path_param("id", "123");
573 request.add_query_param("page", "2");
574 request.add_query_param("limit", "10");
575
576 let user_id: u32 = parameter_extraction::extract_path_param(&request, "id").unwrap();
578 assert_eq!(user_id, 123);
579
580 let page: Option<u32> =
582 parameter_extraction::extract_query_param(&request, "page").unwrap();
583 assert_eq!(page, Some(2));
584
585 let limit: u32 =
587 parameter_extraction::extract_required_query_param(&request, "limit").unwrap();
588 assert_eq!(limit, 10);
589
590 let result = parameter_extraction::extract_path_param::<u32>(&request, "nonexistent");
592 assert!(result.is_err());
593 assert!(matches!(result.unwrap_err(), ParamError::Missing(_)));
594
595 request.add_path_param("invalid", "not_a_number");
597 let result = parameter_extraction::extract_path_param::<u32>(&request, "invalid");
598 assert!(result.is_err());
599 assert!(matches!(result.unwrap_err(), ParamError::ParseError { .. }));
600 }
601
602 #[tokio::test]
603 async fn test_pipeline_stats() {
604 let matcher = RouteMatcherBuilder::new()
605 .get("route1".to_string(), "/route1".to_string())
606 .get("route2".to_string(), "/route2".to_string())
607 .build()
608 .unwrap();
609
610 let middleware_group = MiddlewarePipelineV2::new().add(LoggingMiddleware);
611
612 let pipeline = RequestPipelineBuilder::new()
613 .matcher(matcher)
614 .global_middleware(LoggingMiddleware)
615 .middleware_group("auth", middleware_group)
616 .handler("route1", |_req| Box::pin(async move { ElifResponse::ok() }))
617 .handler("route2", |_req| Box::pin(async move { ElifResponse::ok() }))
618 .build()
619 .unwrap();
620
621 let stats = pipeline.stats();
622 assert_eq!(stats.total_routes, 2);
623 assert_eq!(stats.global_middleware_count, 1);
624 assert_eq!(stats.middleware_groups, 1);
625 assert_eq!(stats.registered_handlers, 2);
626 }
627
628 #[tokio::test]
629 async fn test_arc_optimization_efficiency() {
630 let matcher = RouteMatcherBuilder::new()
632 .get("test_route".to_string(), "/test".to_string())
633 .build()
634 .unwrap();
635
636 let mut builder = RequestPipelineBuilder::new().matcher(matcher);
638
639 for i in 0..100 {
641 builder = builder.handler(format!("handler_{}", i), |_req| {
642 Box::pin(async move { ElifResponse::ok() })
643 });
644 }
645
646 builder = builder.handler("test_route", |_req| {
647 Box::pin(async move { ElifResponse::ok().with_text("Test response") })
648 });
649
650 let pipeline = builder.build().unwrap();
651
652 for _ in 0..10 {
654 let request = ElifRequest::new(
655 crate::request::ElifMethod::GET,
656 "/test".parse().unwrap(),
657 crate::response::ElifHeaderMap::new(),
658 );
659
660 let response = pipeline.process(request).await;
661 assert_eq!(response.status_code(), ElifStatusCode::OK);
662 }
663
664 let stats = pipeline.stats();
666 assert_eq!(stats.registered_handlers, 101);
667
668 }
671
672 #[tokio::test]
673 async fn test_method_not_allowed_error() {
674 let matcher = RouteMatcherBuilder::new()
675 .get("test".to_string(), "/test".to_string())
676 .build()
677 .unwrap();
678
679 let pipeline = RequestPipelineBuilder::new()
680 .matcher(matcher)
681 .handler("test", |_req| {
682 Box::pin(async move { ElifResponse::ok().with_text("Test response") })
683 })
684 .build()
685 .unwrap();
686
687 let connect_method = crate::request::ElifMethod::from_axum(axum::http::Method::CONNECT);
689 let request = ElifRequest::new(
690 connect_method,
691 "/test".parse().unwrap(),
692 crate::response::ElifHeaderMap::new(),
693 );
694
695 let response = pipeline.process(request).await;
696 assert_eq!(response.status_code(), ElifStatusCode::METHOD_NOT_ALLOWED);
697
698 }
702
703 #[tokio::test]
704 async fn test_supported_methods_work() {
705 let matcher = RouteMatcherBuilder::new()
706 .get("get_test".to_string(), "/test".to_string())
707 .post("post_test".to_string(), "/test".to_string())
708 .put("put_test".to_string(), "/test".to_string())
709 .delete("delete_test".to_string(), "/test".to_string())
710 .build()
711 .unwrap();
712
713 let pipeline = RequestPipelineBuilder::new()
714 .matcher(matcher)
715 .handler("get_test", |_req| {
716 Box::pin(async move { ElifResponse::ok().with_text("GET") })
717 })
718 .handler("post_test", |_req| {
719 Box::pin(async move { ElifResponse::ok().with_text("POST") })
720 })
721 .handler("put_test", |_req| {
722 Box::pin(async move { ElifResponse::ok().with_text("PUT") })
723 })
724 .handler("delete_test", |_req| {
725 Box::pin(async move { ElifResponse::ok().with_text("DELETE") })
726 })
727 .build()
728 .unwrap();
729
730 let methods = [
732 (crate::request::ElifMethod::GET, "GET"),
733 (crate::request::ElifMethod::POST, "POST"),
734 (crate::request::ElifMethod::PUT, "PUT"),
735 (crate::request::ElifMethod::DELETE, "DELETE"),
736 ];
737
738 for (method, _expected_response) in methods {
739 let request = ElifRequest::new(
740 method,
741 "/test".parse().unwrap(),
742 crate::response::ElifHeaderMap::new(),
743 );
744
745 let response = pipeline.process(request).await;
746 assert_eq!(response.status_code(), ElifStatusCode::OK);
747 }
748 }
749}