thruster_core/route_tree/
node.rs

1use std::collections::HashMap;
2use smallvec::SmallVec;
3use std::str::Split;
4
5
6use crate::context::Context;
7use thruster_core_async_await::{MiddlewareChain};
8
9// A route with params that may or may not be a terminal node.
10type RouteNodeWithParams<'a, T> = (HashMap<String, String>, bool, &'a MiddlewareChain<T>);
11type RouteNodeEnumeration<T> = SmallVec<[(String, MiddlewareChain<T>, bool); 8]>;
12
13pub struct Node<T: 'static + Context + Send> {
14  runnable: MiddlewareChain<T>,
15  pub children: HashMap<String, Node<T>>,
16  wildcard_node: Option<Box<Node<T>>>,
17  param_key: Option<String>,
18  pub value: String,
19  pub is_wildcard: bool,
20  pub is_terminal_node: bool
21}
22
23impl<T: Context + Send> Clone for Node<T> {
24  fn clone(&self) -> Self {
25    let runnable = self.runnable.clone();
26    let children = self.children.clone();
27
28    let wildcard = if self.is_wildcard {
29      None
30    } else {
31      Some(Box::new(match &self.wildcard_node {
32        Some(wildcard) => (**wildcard).clone(),
33        None => Node::new_wildcard(None)
34      }))
35    };
36
37    Node {
38      runnable,
39      children,
40      wildcard_node: wildcard,
41      value: self.value.clone(),
42      param_key: self.param_key.clone(),
43      is_wildcard: self.is_wildcard,
44      is_terminal_node: self.is_terminal_node
45    }
46  }
47}
48
49impl<T: 'static + Context + Send> Node<T> {
50
51  pub fn new(value: &str) -> Node<T> {
52    Node {
53      runnable: MiddlewareChain::new(),
54      children: HashMap::new(),
55      wildcard_node: Some(Box::new(Node::new_wildcard(None))),
56      value: value.to_owned(),
57      param_key: None,
58      is_wildcard: false,
59      is_terminal_node: false
60    }
61  }
62
63  pub fn new_wildcard(param_name: Option<String>) -> Node<T> {
64    Node {
65      runnable: MiddlewareChain::new(),
66      children: HashMap::new(),
67      wildcard_node: None,
68      value: "*".to_owned(),
69      param_key: param_name,
70      is_wildcard: true,
71      is_terminal_node: false
72    }
73  }
74
75  pub fn add_route(&mut self, route: &str, middleware: MiddlewareChain<T>) {
76    // Strip a leading slash
77    let mut split_iterator = match route.chars().next() {
78      Some('/') => &route[1..],
79      _ => route
80    }.split('/');
81
82    if let Some(piece) = split_iterator.next() {
83      if piece.is_empty() {
84        self.is_terminal_node = true;
85        self.runnable = middleware;
86      } else {
87        match piece.chars().next().unwrap() {
88          ':' => {
89            if !self.is_wildcard {
90              let mut wildcard = Node::new_wildcard(Some(piece[1..].to_owned()));
91              wildcard.is_terminal_node = false;
92
93              wildcard.add_route(&split_iterator.collect::<SmallVec<[&str; 8]>>().join("/"), middleware);
94
95              self.wildcard_node = Some(Box::new(wildcard));
96            }
97          },
98          '*' => {
99            if !self.is_wildcard {
100              let mut wildcard = Node::new_wildcard(None);
101              wildcard.is_terminal_node = true;
102
103              wildcard.add_route(&split_iterator.collect::<SmallVec<[&str; 8]>>().join("/"), middleware);
104
105              self.wildcard_node = Some(Box::new(wildcard));
106            }
107          },
108          _ => {
109            let mut child = self.children.remove(piece)
110              .unwrap_or_else(|| Node::new(piece));
111
112            child.add_route(&split_iterator.collect::<SmallVec<[&str; 8]>>().join("/"), middleware);
113
114            self.children.insert(piece.to_owned(), child);
115          }
116        };
117      }
118    }
119  }
120
121  pub fn add_subtree(&mut self, route: &str, mut subtree: Node<T>) {
122    // Strip a leading slash
123    let mut split_iterator = match route.chars().next() {
124      Some('/') => &route[1..],
125      _ => route
126    }.split('/');
127
128    if let Some(piece) = split_iterator.next() {
129      if piece.is_empty() {
130        if let Some(wildcard_node) = &subtree.wildcard_node {
131          if wildcard_node.param_key.is_some() {
132            if let Some(ref mut existing_wildcard) = self.wildcard_node {
133              for (key, child) in subtree.children.drain() {
134                existing_wildcard.children.insert(key, child);
135              }
136
137              existing_wildcard.param_key = wildcard_node.param_key.clone();
138              existing_wildcard.is_terminal_node = existing_wildcard.is_terminal_node || wildcard_node.is_terminal_node;
139            }
140          } else {
141            for (key, child) in subtree.children.drain() {
142              self.children.insert(key, child);
143            }
144          }
145        }
146
147        self.wildcard_node = subtree.wildcard_node;
148        self.is_terminal_node = subtree.is_terminal_node;
149      } else {
150        let mut child = self.children.remove(piece)
151          .unwrap_or_else(|| Node::new(piece));
152
153        if subtree.runnable.is_assigned() {
154          subtree.runnable.chain(child.runnable.clone());
155          child.runnable = subtree.runnable.clone();
156        }
157
158        child.add_subtree(&split_iterator.collect::<SmallVec<[&str; 8]>>().join("/"), subtree);
159
160        self.children.insert(piece.to_owned(), child);
161      }
162    }
163  }
164
165  pub fn match_route(&self, route: Split<char>) -> RouteNodeWithParams<T> {
166    self.match_route_with_params(route, HashMap::new())
167  }
168
169  pub fn match_route_with_params(&self, mut route: Split<char>, mut params: HashMap<String, String>) -> RouteNodeWithParams<T> {
170    if let Some(piece) = route.next() {
171      match self.children.get(piece) {
172        Some(child) => {
173            let results = child.match_route_with_params(route, params);
174
175            if results.1 {
176              results
177            } else {
178              match &self.wildcard_node {
179                Some(wildcard_node) => (results.0, wildcard_node.is_terminal_node, &wildcard_node.runnable),
180                None => {
181                  if !self.is_wildcard {
182                    results
183                  } else {
184                    (results.0, self.is_terminal_node, &self.runnable)
185                  }
186                }
187              }
188            // }
189          }
190        },
191        None => {
192          match &self.wildcard_node {
193            Some(wildcard_node) => {
194              // Here we check if the current length of the node is 0 or not, if it's
195              // 0 and there is a param key, then this is actually a miss, so return
196              // a non-terminal node (this is the case where the defined route is like
197              // /a/:b, but the incoming route to match is /a/)
198              if piece.is_empty() && wildcard_node.param_key.is_some() {
199                (params, false, &wildcard_node.runnable)
200              } else if piece.is_empty() && wildcard_node.param_key.is_none() {
201                (params, wildcard_node.is_terminal_node, &wildcard_node.runnable)
202              } else {
203                if let Some(param_key) = &wildcard_node.param_key {
204                  params.insert(param_key.to_owned(), piece.to_owned());
205                }
206
207                let results = wildcard_node.match_route_with_params(route, params);
208
209                // If the wildcard isn't a terminal node, then try to return this
210                // wildcard
211                if results.1 {
212                  results
213                } else {
214                  (results.0, wildcard_node.is_terminal_node, &self.runnable)
215                }
216              }
217            }
218            None => (params, self.is_terminal_node, &self.runnable)
219          }
220        }
221      }
222    } else if self.is_terminal_node {
223        (params, self.is_terminal_node, &self.runnable)
224    } else if let Some(wildcard_node) = &self.wildcard_node {
225      if wildcard_node.param_key == None {
226        let results = wildcard_node.match_route_with_params(route, params);
227
228        // If the wildcard isn't a terminal node, then try to return this
229        // wildcard
230        if results.1 {
231          results
232        } else {
233          (results.0, self.is_terminal_node, &self.runnable)
234        }
235      } else {
236        (params, self.is_terminal_node, &self.runnable)
237      }
238    } else {
239      (params, self.is_terminal_node, &self.runnable)
240    }
241  }
242
243  /// Used mostly for debugging the route tree
244  /// Example usage
245  /// ```ignore
246  ///   let mut app = App::create(generate_context);
247  ///
248  ///   app.get("/plaintext", middleware![plaintext]);
249  ///  println!("app: {}", app._route_parser.route_tree.root_node.tree_string(""));
250  ///  for (route, middleware) in app._route_parser.route_tree.root_node.get_route_list() {
251  ///    println!("{}: {}", route, middleware.len());
252  ///  }
253  /// ```
254  pub fn tree_string(&self, indent: &str) -> String {
255    let mut in_progress = "".to_owned();
256    let value = match self.param_key.clone() {
257      Some(key) => format!(":{}", key),
258      None => self.value.to_owned()
259    };
260
261    in_progress = format!("{}\n{}{}: {}, {}",
262      in_progress,
263      indent,
264      value,
265      self.runnable.is_assigned(),
266      self.is_terminal_node);
267
268    for child in self.children.values() {
269      in_progress = format!("{}{}", in_progress, child.tree_string(&format!("{}  ", indent)));
270    }
271
272    if let Some(wildcard_node) = &self.wildcard_node {
273      in_progress = format!("{}{}", in_progress, wildcard_node.tree_string(&format!("{}  ", indent)));
274    }
275
276    in_progress
277  }
278
279  pub fn get_route_list(&self) -> RouteNodeEnumeration<T> {
280    let mut routes = SmallVec::new();
281
282    let self_route = &self.value;
283
284    // Add child routes
285    for child in self.children.values() {
286      if child.is_terminal_node {
287        routes.push((
288          format!("{}/{}", self_route, child.value),
289          child.runnable.clone(),
290          child.is_terminal_node
291        ));
292      }
293
294      for child_route in child.get_route_list() {
295        routes.push((
296          format!("{}/{}", self_route, child_route.0),
297          child_route.1.clone(),
298          child_route.2
299        ));
300      }
301    }
302
303    if let Some(ref wildcard_node) = self.wildcard_node {
304      let piece = match wildcard_node.param_key {
305        Some(ref param_key) => format!(":{}", param_key),
306        None => wildcard_node.value.clone()
307      };
308
309      routes.push((
310        format!("{}/{}", self_route, piece),
311        wildcard_node.runnable.clone(),
312        wildcard_node.is_terminal_node
313      ));
314    }
315
316    routes
317  }
318
319  ///
320  /// Pushes middleware down to the leaf nodes, accumulating along the way. This is helpful for
321  /// propagating generic middleware down the stack
322  ///
323  pub fn push_middleware_to_populated_nodes(&mut self, other_node: &Node<T>, accumulated_middleware: &MiddlewareChain<T>) {
324    let fake_node = Node::new("");
325
326    let accumulating_chain = if other_node.runnable.is_assigned() {
327      let mut accumulating_chain = other_node.runnable.clone();
328      accumulating_chain.chain(accumulated_middleware.clone());
329      accumulating_chain
330    } else {
331      accumulated_middleware.clone()
332    };
333
334    if self.runnable.is_assigned() {
335      let mut other = other_node.runnable.clone();
336      let mut other_2 = other.clone();
337      let mut accumulating_chain = accumulated_middleware.clone();
338      let old = self.runnable.clone();
339
340      other_2.chain(old);
341      accumulating_chain.chain(other_2);
342      other.chain(accumulating_chain);
343
344      self.runnable = other;
345    }
346
347    // Match children, recurse if child match
348    for (key, child) in &mut self.children {
349      // Traverse the child tree, or else make a dummy node
350      let other_child = other_node.children.get(key).unwrap_or(&fake_node);
351
352      child.push_middleware_to_populated_nodes(other_child, &accumulating_chain.clone());
353    }
354
355    // Copy over wildcards
356    if let Some(ref mut wildcard_node) = self.wildcard_node {
357      if let Some(other_wildcard_node) = &other_node.wildcard_node {
358        wildcard_node.push_middleware_to_populated_nodes(other_wildcard_node, &accumulating_chain);
359      }
360    }
361  }
362}