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#[derive(Clone)]
12pub struct Route {
13 r#type: RequestType,
14 path: String,
15 f: Option<BoxedCallable>,
16 routes: Routes,
17}
18
19impl Route {
20 #[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 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 #[must_use]
47 pub const fn get_type(&self) -> RequestType {
48 self.r#type
49 }
50
51 #[must_use]
53 pub fn get_path(&self) -> String {
54 self.clone().path
55 }
56
57 #[must_use]
59 pub fn get_callable(&self) -> Option<BoxedCallable> {
60 self.clone().f
61 }
62
63 #[must_use]
65 pub fn get_routes(&self) -> Routes {
66 self.clone().routes
67 }
68
69 #[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#[must_use]
117pub fn service(path: &str, routes: Routes) -> Route {
118 Route::service(path, routes)
119}
120
121pub fn get<F: CloneableFn<Output = ServerResponse> + 'static>(path: &str, f: F) -> Route {
123 Route::route(RequestType::Get, path, f)
124}
125
126pub fn post<F: CloneableFn<Output = ServerResponse> + 'static>(path: &str, f: F) -> Route {
128 Route::route(RequestType::Post, path, f)
129}
130
131pub fn put<F: CloneableFn<Output = ServerResponse> + 'static>(path: &str, f: F) -> Route {
133 Route::route(RequestType::Put, path, f)
134}
135
136pub fn patch<F: CloneableFn<Output = ServerResponse> + 'static>(path: &str, f: F) -> Route {
138 Route::route(RequestType::Patch, path, f)
139}
140
141pub fn delete<F: CloneableFn<Output = ServerResponse> + 'static>(path: &str, f: F) -> Route {
143 Route::route(RequestType::Delete, path, f)
144}