titan_router/
router.rs

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); // BIG PROBLEM FATAL TODO:
131    assert!(router.lookup("/ni").is_none()); // BIG PROBLEM FATAL TODO:
132  }
133}