1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
mod node;

use std::collections::HashMap;

pub use self::node::Node;
use crate::context::Context;
use thruster_core_async_await::{MiddlewareChain};

pub enum Method {
  DELETE,
  GET,
  OPTIONS,
  POST,
  PUT,
  UPDATE
}

pub struct RouteTree<T: 'static + Context + Send> {
  pub root_node: Node<T>,
  pub generic_root_node: Node<T>,
  pub specific_root_node: Node<T>
}

fn method_to_prefix<'a>(method: &Method) -> &'a str {
  match method {
    Method::DELETE => "__DELETE__",
    Method::GET => "__GET__",
    Method::OPTIONS => "__OPTIONS__",
    Method::POST => "__POST__",
    Method::PUT => "__PUT__",
    Method::UPDATE => "__UPDATE__"
  }
}

impl<T: 'static + Context + Send> RouteTree<T> {
  pub fn new() -> RouteTree<T> {
    RouteTree {
      root_node: Node::new(""),
      generic_root_node: Node::new(""),
      specific_root_node: Node::new("")
    }
  }

  ///
  /// Updates the `root_node` of the tree by merging the generic tree into the specific tree. This
  /// is necessary after adding any routes to ensure that the matching functions of the tree are
  /// up to date.
  ///
  pub fn update_root_node(&mut self) {
    let mut root_node = self.specific_root_node.clone();

    root_node.push_middleware_to_populated_nodes(&self.generic_root_node, &MiddlewareChain::new());

    self.root_node = root_node;
  }

  pub fn add_use_node(&mut self, route: &str, middleware: MiddlewareChain<T>) {
    self.generic_root_node.add_route(route, middleware);
    self.update_root_node();
  }

  pub fn add_route_with_method(&mut self, method: &Method, route: &str, middleware: MiddlewareChain<T>) {
    let prefix = method_to_prefix(&method);

    let full_route = format!("{}{}", prefix, route);

    self.specific_root_node.add_route(&full_route, middleware);
    self.update_root_node();
  }

  pub fn add_route(&mut self, route: &str, middleware: MiddlewareChain<T>) {
    self.specific_root_node.add_route(route, middleware);
    self.update_root_node();
  }

  fn _adopt_sub_app_method_to_self(&mut self, route: &str, mut route_tree: RouteTree<T>, method: &Method) -> RouteTree<T> {
    let method_prefix = method_to_prefix(&method);

    let mut self_routes = self.specific_root_node.children.remove(method_prefix)
      .unwrap_or_else(|| Node::new(method_prefix));

    if let Some(tree_routes) = route_tree.root_node.children.remove(method_prefix) {
      self_routes.add_subtree(route, tree_routes);
    }

    self.specific_root_node.children.insert(method_prefix.to_owned(), self_routes);

    // Return ownership
    route_tree
  }

  pub fn add_route_tree(&mut self, route: &str, mut route_tree: RouteTree<T>) {
    route_tree = self._adopt_sub_app_method_to_self(route, route_tree, &Method::DELETE);
    route_tree = self._adopt_sub_app_method_to_self(route, route_tree, &Method::GET);
    route_tree = self._adopt_sub_app_method_to_self(route, route_tree, &Method::OPTIONS);
    route_tree = self._adopt_sub_app_method_to_self(route, route_tree, &Method::POST);
    route_tree = self._adopt_sub_app_method_to_self(route, route_tree, &Method::PUT);
    self._adopt_sub_app_method_to_self(route, route_tree, &Method::UPDATE);
    self.update_root_node();
  }

  pub fn match_route(&self, route: &str) -> (HashMap<String, String>, &MiddlewareChain<T>) {
    let results = self.root_node.match_route(route.split('/'));

    (results.0, results.2)
  }

  pub fn match_route_with_params(&self, route: &str, params: HashMap<String, String>) -> (HashMap<String, String>, &MiddlewareChain<T>) {
    let results = self.root_node.match_route_with_params(route.split('/'), params);

    (results.0, results.2)
  }
}

impl<T: 'static + Context + Send> Default for RouteTree<T> {
  fn default() -> RouteTree<T> {
    RouteTree::new()
  }
}