rustisan_core/
routing.rs

1//! Routing module for the Rustisan framework
2//!
3//! This module provides Laravel-style routing functionality with support for
4//! HTTP methods, route parameters, middleware, and route groups.
5
6use std::collections::HashMap;
7use std::fmt;
8use std::future::Future;
9use std::pin::Pin;
10use std::sync::Arc;
11
12use axum::{
13    extract::{Path, Query, State},
14    http::{Method, StatusCode},
15    response::{IntoResponse, Response as AxumResponse},
16    Json,
17};
18use serde_json::{json, Value};
19
20use crate::{
21    errors::{Result, RustisanError},
22    http::{Request, Response},
23};
24
25/// Type alias for async route handlers
26pub type AsyncHandler = Arc<
27    dyn Fn() -> Pin<Box<dyn Future<Output = Response> + Send>> + Send + Sync,
28>;
29
30/// Type alias for async handlers with request parameter
31pub type AsyncHandlerWithRequest = Arc<
32    dyn Fn(Request) -> Pin<Box<dyn Future<Output = Response> + Send>> + Send + Sync,
33>;
34
35/// Type alias for async handlers with ID parameter
36pub type AsyncHandlerWithId = Arc<
37    dyn Fn(u32) -> Pin<Box<dyn Future<Output = Response> + Send>> + Send + Sync,
38>;
39
40/// Type alias for async handlers with ID and request parameters
41pub type AsyncHandlerWithIdAndRequest = Arc<
42    dyn Fn(u32, Request) -> Pin<Box<dyn Future<Output = Response> + Send>> + Send + Sync,
43>;
44
45/// Route definition
46#[derive(Clone)]
47pub struct Route {
48    /// HTTP method
49    pub method: Method,
50    /// Route path
51    pub path: String,
52    /// Route handler
53    pub handler: RouteHandler,
54    /// Route middleware
55    pub middleware: Vec<String>,
56    /// Route name
57    pub name: Option<String>,
58}
59
60/// Route handler variants
61#[derive(Clone)]
62pub enum RouteHandler {
63    /// Simple async handler
64    Simple(AsyncHandler),
65    /// Handler with request parameter
66    WithRequest(AsyncHandlerWithRequest),
67    /// Handler with ID parameter
68    WithId(AsyncHandlerWithId),
69    /// Handler with ID and request parameters
70    WithIdAndRequest(AsyncHandlerWithIdAndRequest),
71}
72
73/// Router for managing routes
74pub struct Router {
75    /// All registered routes
76    pub routes: Vec<Route>,
77    /// Route groups
78    pub groups: Vec<RouteGroup>,
79    /// Global middleware
80    pub middleware: Vec<String>,
81    /// Named routes for URL generation
82    named_routes: HashMap<String, usize>,
83}
84
85/// Route group for organizing related routes
86pub struct RouteGroup {
87    /// Group prefix
88    pub prefix: String,
89    /// Group middleware
90    pub middleware: Vec<String>,
91    /// Routes in this group
92    pub routes: Vec<Route>,
93}
94
95impl Router {
96    /// Creates a new router
97    pub fn new() -> Self {
98        Self {
99            routes: Vec::new(),
100            groups: Vec::new(),
101            middleware: Vec::new(),
102            named_routes: HashMap::new(),
103        }
104    }
105
106    /// Adds a GET route
107    pub fn get<F, Fut>(&mut self, path: &str, handler: F) -> &mut Self
108    where
109        F: Fn() -> Fut + Send + Sync + 'static,
110        Fut: Future<Output = Response> + Send + 'static,
111    {
112        let async_handler: AsyncHandler = Arc::new(move || {
113            let fut = handler();
114            Box::pin(fut)
115        });
116
117        self.routes.push(Route {
118            method: Method::GET,
119            path: path.to_string(),
120            handler: RouteHandler::Simple(async_handler),
121            middleware: Vec::new(),
122            name: None,
123        });
124
125        self
126    }
127
128    /// Adds a POST route
129    pub fn post<F, Fut>(&mut self, path: &str, handler: F) -> &mut Self
130    where
131        F: Fn(Request) -> Fut + Send + Sync + 'static,
132        Fut: Future<Output = Response> + Send + 'static,
133    {
134        let async_handler: AsyncHandlerWithRequest = Arc::new(move |req| {
135            let fut = handler(req);
136            Box::pin(fut)
137        });
138
139        self.routes.push(Route {
140            method: Method::POST,
141            path: path.to_string(),
142            handler: RouteHandler::WithRequest(async_handler),
143            middleware: Vec::new(),
144            name: None,
145        });
146
147        self
148    }
149
150    /// Adds a PUT route
151    pub fn put<F, Fut>(&mut self, path: &str, handler: F) -> &mut Self
152    where
153        F: Fn(u32, Request) -> Fut + Send + Sync + 'static,
154        Fut: Future<Output = Response> + Send + 'static,
155    {
156        let async_handler: AsyncHandlerWithIdAndRequest = Arc::new(move |id, req| {
157            let fut = handler(id, req);
158            Box::pin(fut)
159        });
160
161        self.routes.push(Route {
162            method: Method::PUT,
163            path: path.to_string(),
164            handler: RouteHandler::WithIdAndRequest(async_handler),
165            middleware: Vec::new(),
166            name: None,
167        });
168
169        self
170    }
171
172    /// Adds a PATCH route
173    pub fn patch<F, Fut>(&mut self, path: &str, handler: F) -> &mut Self
174    where
175        F: Fn(u32, Request) -> Fut + Send + Sync + 'static,
176        Fut: Future<Output = Response> + Send + 'static,
177    {
178        let async_handler: AsyncHandlerWithIdAndRequest = Arc::new(move |id, req| {
179            let fut = handler(id, req);
180            Box::pin(fut)
181        });
182
183        self.routes.push(Route {
184            method: Method::PATCH,
185            path: path.to_string(),
186            handler: RouteHandler::WithIdAndRequest(async_handler),
187            middleware: Vec::new(),
188            name: None,
189        });
190
191        self
192    }
193
194    /// Adds a DELETE route
195    pub fn delete<F, Fut>(&mut self, path: &str, handler: F) -> &mut Self
196    where
197        F: Fn(u32) -> Fut + Send + Sync + 'static,
198        Fut: Future<Output = Response> + Send + 'static,
199    {
200        let async_handler: AsyncHandlerWithId = Arc::new(move |id| {
201            let fut = handler(id);
202            Box::pin(fut)
203        });
204
205        self.routes.push(Route {
206            method: Method::DELETE,
207            path: path.to_string(),
208            handler: RouteHandler::WithId(async_handler),
209            middleware: Vec::new(),
210            name: None,
211        });
212
213        self
214    }
215
216    /// Creates a route group
217    pub fn group<F>(&mut self, prefix: &str, group_fn: F) -> &mut Self
218    where
219        F: FnOnce(&mut RouteGroup),
220    {
221        let mut group = RouteGroup {
222            prefix: prefix.to_string(),
223            middleware: Vec::new(),
224            routes: Vec::new(),
225        };
226
227        group_fn(&mut group);
228        self.groups.push(group);
229
230        self
231    }
232
233    /// Adds global middleware
234    pub fn middleware(&mut self, middleware: Vec<String>) -> &mut Self {
235        self.middleware.extend(middleware);
236        self
237    }
238
239    /// Names the last added route
240    pub fn name(&mut self, name: &str) -> &mut Self {
241        if let Some(route) = self.routes.last_mut() {
242            route.name = Some(name.to_string());
243            let index = self.routes.len() - 1;
244            self.named_routes.insert(name.to_string(), index);
245        }
246        self
247    }
248
249    /// Finds a route by name
250    pub fn find_route(&self, name: &str) -> Option<&Route> {
251        self.named_routes.get(name).map(|&idx| &self.routes[idx])
252    }
253
254    /// Generates a URL for a named route
255    pub fn url(&self, name: &str, params: Option<HashMap<String, String>>) -> Result<String> {
256        if let Some(route) = self.find_route(name) {
257            let mut url = route.path.clone();
258
259            if let Some(params) = params {
260                for (key, value) in params {
261                    url = url.replace(&format!(":{}", key), &value);
262                }
263            }
264
265            Ok(url)
266        } else {
267            Err(RustisanError::RoutingError(format!(
268                "Route '{}' not found",
269                name
270            )))
271        }
272    }
273
274    /// Gets all routes including group routes
275    pub fn all_routes(&self) -> Vec<&Route> {
276        let mut all_routes = Vec::new();
277
278        // Add individual routes
279        all_routes.extend(self.routes.iter());
280
281        // Add group routes
282        for group in &self.groups {
283            all_routes.extend(group.routes.iter());
284        }
285
286        all_routes
287    }
288
289    /// Gets route count
290    pub fn route_count(&self) -> usize {
291        let group_routes: usize = self.groups.iter().map(|g| g.routes.len()).sum();
292        self.routes.len() + group_routes
293    }
294}
295
296impl RouteGroup {
297    /// Adds a GET route to the group
298    pub fn get<F, Fut>(&mut self, path: &str, handler: F) -> &mut Self
299    where
300        F: Fn() -> Fut + Send + Sync + 'static,
301        Fut: Future<Output = Response> + Send + 'static,
302    {
303        let async_handler: AsyncHandler = Arc::new(move || {
304            let fut = handler();
305            Box::pin(fut)
306        });
307
308        let full_path = format!("{}{}", self.prefix, path);
309
310        self.routes.push(Route {
311            method: Method::GET,
312            path: full_path,
313            handler: RouteHandler::Simple(async_handler),
314            middleware: self.middleware.clone(),
315            name: None,
316        });
317
318        self
319    }
320
321    /// Adds a GET route with ID parameter to the group
322    pub fn get_with_id<F, Fut>(&mut self, path: &str, handler: F) -> &mut Self
323    where
324        F: Fn(u32) -> Fut + Send + Sync + 'static,
325        Fut: Future<Output = Response> + Send + 'static,
326    {
327        let async_handler: AsyncHandlerWithId = Arc::new(move |id| {
328            let fut = handler(id);
329            Box::pin(fut)
330        });
331
332        let full_path = format!("{}{}", self.prefix, path);
333
334        self.routes.push(Route {
335            method: Method::GET,
336            path: full_path,
337            handler: RouteHandler::WithId(async_handler),
338            middleware: self.middleware.clone(),
339            name: None,
340        });
341
342        self
343    }
344
345    /// Adds a POST route to the group
346    pub fn post<F, Fut>(&mut self, path: &str, handler: F) -> &mut Self
347    where
348        F: Fn(Request) -> Fut + Send + Sync + 'static,
349        Fut: Future<Output = Response> + Send + 'static,
350    {
351        let async_handler: AsyncHandlerWithRequest = Arc::new(move |req| {
352            let fut = handler(req);
353            Box::pin(fut)
354        });
355
356        let full_path = format!("{}{}", self.prefix, path);
357
358        self.routes.push(Route {
359            method: Method::POST,
360            path: full_path,
361            handler: RouteHandler::WithRequest(async_handler),
362            middleware: self.middleware.clone(),
363            name: None,
364        });
365
366        self
367    }
368
369    /// Adds a PUT route to the group
370    pub fn put<F, Fut>(&mut self, path: &str, handler: F) -> &mut Self
371    where
372        F: Fn(u32, Request) -> Fut + Send + Sync + 'static,
373        Fut: Future<Output = Response> + Send + 'static,
374    {
375        let async_handler: AsyncHandlerWithIdAndRequest = Arc::new(move |id, req| {
376            let fut = handler(id, req);
377            Box::pin(fut)
378        });
379
380        let full_path = format!("{}{}", self.prefix, path);
381
382        self.routes.push(Route {
383            method: Method::PUT,
384            path: full_path,
385            handler: RouteHandler::WithIdAndRequest(async_handler),
386            middleware: self.middleware.clone(),
387            name: None,
388        });
389
390        self
391    }
392
393    /// Adds a DELETE route to the group
394    pub fn delete<F, Fut>(&mut self, path: &str, handler: F) -> &mut Self
395    where
396        F: Fn(u32) -> Fut + Send + Sync + 'static,
397        Fut: Future<Output = Response> + Send + 'static,
398    {
399        let async_handler: AsyncHandlerWithId = Arc::new(move |id| {
400            let fut = handler(id);
401            Box::pin(fut)
402        });
403
404        let full_path = format!("{}{}", self.prefix, path);
405
406        self.routes.push(Route {
407            method: Method::DELETE,
408            path: full_path,
409            handler: RouteHandler::WithId(async_handler),
410            middleware: self.middleware.clone(),
411            name: None,
412        });
413
414        self
415    }
416
417    /// Adds middleware to the group
418    pub fn middleware(&mut self, middleware: Vec<String>) -> &mut Self {
419        self.middleware.extend(middleware);
420        self
421    }
422}
423
424impl Default for Router {
425    fn default() -> Self {
426        Self::new()
427    }
428}
429
430impl fmt::Debug for Route {
431    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
432        f.debug_struct("Route")
433            .field("method", &self.method)
434            .field("path", &self.path)
435            .field("middleware", &self.middleware)
436            .field("name", &self.name)
437            .finish()
438    }
439}
440
441impl fmt::Debug for RouteHandler {
442    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
443        match self {
444            RouteHandler::Simple(_) => write!(f, "Simple"),
445            RouteHandler::WithRequest(_) => write!(f, "WithRequest"),
446            RouteHandler::WithId(_) => write!(f, "WithId"),
447            RouteHandler::WithIdAndRequest(_) => write!(f, "WithIdAndRequest"),
448        }
449    }
450}
451
452/// Helper function to create a success response
453pub fn create_success_response(data: Value) -> Result<Response> {
454    Response::success(data, None)
455}
456
457/// Helper function to create an error response
458pub fn create_error_response(message: &str) -> Result<Response> {
459    Response::bad_request(message)
460}
461
462/// Helper function to create a JSON response
463pub fn create_json_response(data: Value) -> Result<Response> {
464    Response::json(data)
465}
466
467/// Helper function to create a response with custom status
468pub fn create_response_with_status(status: StatusCode, data: Value) -> Result<Response> {
469    Ok(Response::new(status, data))
470}
471
472/// Macro to simplify route registration
473#[macro_export]
474macro_rules! routes {
475    ($router:expr, {
476        $($method:ident $path:literal => $handler:expr),* $(,)?
477    }) => {
478        $(
479            $router.$method($path, $handler);
480        )*
481    };
482}
483
484/// Macro to simplify route group registration
485#[macro_export]
486macro_rules! route_group {
487    ($router:expr, $prefix:literal, {
488        $($method:ident $path:literal => $handler:expr),* $(,)?
489    }) => {
490        $router.group($prefix, |group| {
491            $(
492                group.$method($path, $handler);
493            )*
494        });
495    };
496}
497
498#[cfg(test)]
499mod tests {
500    use super::*;
501
502    #[tokio::test]
503    async fn test_router_creation() {
504        let router = Router::new();
505        assert!(router.routes.is_empty());
506        assert!(router.groups.is_empty());
507        assert_eq!(router.route_count(), 0);
508    }
509
510    #[tokio::test]
511    async fn test_add_get_route() {
512        let mut router = Router::new();
513        router.get("/test", || async {
514            Response::ok("Hello").unwrap()
515        });
516
517        assert_eq!(router.routes.len(), 1);
518        assert_eq!(router.routes[0].method, Method::GET);
519        assert_eq!(router.routes[0].path, "/test");
520    }
521
522    #[tokio::test]
523    async fn test_route_group() {
524        let mut router = Router::new();
525        router.group("/api", |group| {
526            group.get("/users", || async {
527                Response::ok("Users").unwrap()
528            });
529        });
530
531        assert_eq!(router.groups.len(), 1);
532        assert_eq!(router.groups[0].prefix, "/api");
533        assert_eq!(router.groups[0].routes.len(), 1);
534        assert_eq!(router.groups[0].routes[0].path, "/api/users");
535    }
536
537    #[tokio::test]
538    async fn test_named_routes() {
539        let mut router = Router::new();
540        router
541            .get("/users", || async {
542                Response::ok("Users").unwrap()
543            })
544            .name("users.index");
545
546        assert!(router.find_route("users.index").is_some());
547
548        let url = router.url("users.index", None).unwrap();
549        assert_eq!(url, "/users");
550    }
551
552    #[tokio::test]
553    async fn test_route_with_parameters() {
554        let mut router = Router::new();
555        router
556            .get("/users/:id", || async {
557                Response::ok("User").unwrap()
558            })
559            .name("users.show");
560
561        let mut params = HashMap::new();
562        params.insert("id".to_string(), "123".to_string());
563
564        let url = router.url("users.show", Some(params)).unwrap();
565        assert_eq!(url, "/users/123");
566    }
567
568    #[test]
569    fn test_helper_functions() {
570        let response = create_success_response(json!({"message": "test"}));
571        assert!(response.is_ok());
572
573        let response = create_error_response("error message");
574        assert!(response.is_ok());
575
576        let response = create_json_response(json!({"data": "test"}));
577        assert!(response.is_ok());
578    }
579
580    #[test]
581    fn test_route_count() {
582        let mut router = Router::new();
583
584        router.get("/", || async {
585            Response::ok("Home").unwrap()
586        });
587
588        router.group("/api", |group| {
589            group.get("/users", || async {
590                Response::ok("Users").unwrap()
591            });
592            group.get("/posts", || async {
593                Response::ok("Posts").unwrap()
594            });
595        });
596
597        assert_eq!(router.route_count(), 3);
598    }
599}