1use std::collections::HashMap;
2
3use crate::segments::{FindSegmentResult, Segments};
4
5#[derive(Clone)]
6pub struct Router<V>
7where
8 V: Clone,
9{
10 routes: Vec<(Segments, V)>,
11 lookup_cache: HashMap<Segments, V>,
12}
13
14impl<V> Default for Router<V>
15where
16 V: Clone,
17{
18 fn default() -> Self {
19 Self { routes: Vec::default(), lookup_cache: HashMap::default() }
20 }
21}
22
23#[derive(Debug, PartialEq)]
24pub struct Match<V> {
25 pub value: V,
26 pub params: HashMap<String, String>,
27}
28
29impl<V> IntoIterator for Router<V>
30where
31 V: Clone,
32{
33 type Item = (Segments, V);
34 type IntoIter = std::vec::IntoIter<Self::Item>;
35 fn into_iter(self) -> Self::IntoIter {
36 let mut vec: Vec<(Segments, V)> = self.lookup_cache.into_iter().collect();
37
38 vec.extend(self.routes);
39 vec.into_iter()
40 }
41}
42
43impl<V> Router<V>
44where
45 V: Clone,
46{
47 pub fn at(&mut self, route: &str, handler: V) {
48 let segments: Segments = route.to_string().into();
49
50 if segments.num_params() == 0 {
51 self.lookup_cache.insert(segments, handler);
52 } else {
53 self.routes.push((segments.clone(), handler));
54 }
55 }
56
57 pub fn find(&mut self, route: &str) -> Option<Match<&V>> {
58 let segments = Segments::from(route.to_string());
59
60 if let Some(value) = self.lookup_cache.get(&segments) {
61 return Some(Match { value, params: HashMap::default() });
62 };
63
64 for (key, value) in self.routes.iter() {
65 if let FindSegmentResult::Match(params) = key.find(&segments.0) {
66 return Some(Match { value, params });
67 };
68 }
69 None
70 }
71
72 pub fn lookup(&self, route: &str) -> Option<Match<&V>> {
73 let from_request = Segments::from(route.to_string());
74
75 if let Some(value) = self.lookup_cache.get(&from_request) {
76 return Some(Match { value, params: HashMap::default() });
77 };
78
79 for (contract, value) in &self.routes {
80 if let FindSegmentResult::Match(params) = contract.find(&from_request.0) {
81 return Some(Match { value, params });
82 };
83 }
84 None
85 }
86 pub fn lookup_mut(&mut self, route: &str) -> Option<Match<&mut V>> {
87 let segments = Segments::from(route.to_string());
88
89 if let Some(value) = self.lookup_cache.get_mut(&segments) {
90 return Some(Match { value, params: HashMap::default() });
91 };
92
93 for (key, value) in self.routes.iter_mut() {
94 if let FindSegmentResult::Match(params) = key.find(&segments.0) {
95 return Some(Match { value, params });
96 };
97 }
98 None
99 }
100}
101
102#[cfg(test)]
103mod lib_tests {
104 use super::*;
105 #[test]
106 fn test() {
107 let mut router = Router::default();
108
109 let nice = "nice".to_string();
110
111 router.at("/test/testing2", "testing2".to_string());
112 router.at("/test/:var/:fdshj", nice.clone());
113 router.at("/test/:var", ":var".to_string());
114 router.at("/test2", nice.clone());
115
116 router.at("/nice", "nice2".to_string());
117
118 if let Some(_static) = router.lookup("/test/testing2") {
119 if _static.value == "testing2" {
120 assert_eq!(_static.params.len(), 0);
121 }
122 };
123
124 if let Some(_static) = router.lookup("/test/testing") {
125 if _static.value == ":var" {
126 assert_eq!(_static.params.len(), 1);
127 }
128 };
129
130 assert_eq!(router.lookup("/te"), None); assert!(router.lookup("/ni").is_none()); }
133}