nova_router/
route.rs

1use std::fmt::{Debug, Formatter};
2use std::hash::{Hash, Hasher};
3
4use nova_core::response::ServerResponse;
5use nova_core::types::request_type::RequestType;
6
7use crate::callable::{BoxedCallable, CloneableFn};
8use crate::routes::Routes;
9
10/// Nova Route structure
11#[derive(Clone)]
12pub struct Route {
13    r#type: RequestType,
14    path: String,
15    f: Option<BoxedCallable>,
16    routes: Routes,
17}
18
19impl Route {
20    /// create new service route
21    #[must_use]
22    pub fn service(path: &str, routes: Routes) -> Self {
23        Self {
24            r#type: RequestType::Get,
25            path: path.to_string(),
26            f: None,
27            routes,
28        }
29    }
30
31    /// create new route
32    pub fn route<F: CloneableFn<Output = ServerResponse> + 'static>(
33        r#type: RequestType,
34        path: &str,
35        f: F,
36    ) -> Self {
37        Self {
38            r#type,
39            path: path.to_string(),
40            f: Some(Box::new(f)),
41            routes: Routes::default(),
42        }
43    }
44
45    /// Get request type
46    #[must_use]
47    pub const fn get_type(&self) -> RequestType {
48        self.r#type
49    }
50
51    /// Get path string
52    #[must_use]
53    pub fn get_path(&self) -> String {
54        self.clone().path
55    }
56
57    /// Get callable
58    #[must_use]
59    pub fn get_callable(&self) -> Option<BoxedCallable> {
60        self.clone().f
61    }
62
63    /// Get routes
64    #[must_use]
65    pub fn get_routes(&self) -> Routes {
66        self.clone().routes
67    }
68
69    /// Check if route matches predicate
70    #[must_use]
71    pub fn matches(&self, r#type: RequestType, path: &str) -> bool {
72        if self.r#type != r#type {
73            return false;
74        }
75        let self_segments = self
76            .path
77            .split('/')
78            .filter(|s| !s.is_empty())
79            .collect::<Vec<&str>>();
80        let segments = path
81            .split('/')
82            .filter(|s| !s.is_empty())
83            .collect::<Vec<&str>>();
84        if self_segments.len() != segments.len() {
85            return false;
86        }
87        self_segments
88            .into_iter()
89            .zip(segments)
90            .all(|(s, t)| s == t || (s.starts_with('{') && s.ends_with('}')))
91    }
92}
93
94impl Debug for Route {
95    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
96        write!(f, "{} {}", self.r#type, self.path)
97    }
98}
99
100impl PartialEq<Self> for Route {
101    fn eq(&self, other: &Self) -> bool {
102        self.r#type == other.r#type && self.path == other.path
103    }
104}
105
106impl Eq for Route {}
107
108impl Hash for Route {
109    fn hash<H: Hasher>(&self, state: &mut H) {
110        self.r#type.hash(state);
111        self.path.hash(state);
112    }
113}
114
115/// Create service route
116#[must_use]
117pub fn service(path: &str, routes: Routes) -> Route {
118    Route::service(path, routes)
119}
120
121/// Create GET route
122pub fn get<F: CloneableFn<Output = ServerResponse> + 'static>(path: &str, f: F) -> Route {
123    Route::route(RequestType::Get, path, f)
124}
125
126/// Create POST route
127pub fn post<F: CloneableFn<Output = ServerResponse> + 'static>(path: &str, f: F) -> Route {
128    Route::route(RequestType::Post, path, f)
129}
130
131/// Create PUT route
132pub fn put<F: CloneableFn<Output = ServerResponse> + 'static>(path: &str, f: F) -> Route {
133    Route::route(RequestType::Put, path, f)
134}
135
136/// Create PATCH route
137pub fn patch<F: CloneableFn<Output = ServerResponse> + 'static>(path: &str, f: F) -> Route {
138    Route::route(RequestType::Patch, path, f)
139}
140
141/// Create DELETE route
142pub fn delete<F: CloneableFn<Output = ServerResponse> + 'static>(path: &str, f: F) -> Route {
143    Route::route(RequestType::Delete, path, f)
144}