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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
use async_trait::async_trait;
use hyper::{Body, Method, Request, Response};
use std::collections::HashMap;
#[derive(Clone)]
pub enum PathPart {
Exact { value: String },
Parameter { name: String },
}
#[async_trait]
pub trait RouteHandler {
async fn execute(
&self,
request: &mut Request<Body>,
path_parameters: HashMap<String, String>,
request_id: String,
) -> Result<Response<Body>, hyper::Error>;
}
pub struct Route {
pub method: Method,
pub path: Vec<PathPart>,
pub handler: Box<dyn RouteHandler + Sync + Send>,
}
#[derive(thiserror::Error, Debug)]
pub enum RouteRegistrationError {
#[error("A route is already registered that conflicts with this route")]
RouteConflict,
}
#[derive(Default)]
pub struct RoutingTable {
routes: HashMap<Method, RouteNode>,
}
#[derive(PartialEq, Eq, Hash)]
enum SearchablePathPart {
Exact(String),
Parameter,
}
struct RouteNode {
leaf: Option<Route>,
children: HashMap<SearchablePathPart, RouteNode>,
}
impl RoutingTable {
pub fn new() -> Self {
Default::default()
}
pub fn register(&mut self, route: Route) -> Result<(), RouteRegistrationError> {
let mut node = self
.routes
.entry(route.method.clone())
.or_insert(RouteNode {
leaf: None,
children: HashMap::new(),
});
for part in &route.path {
let searchable_part = match part {
PathPart::Exact { value: name } => SearchablePathPart::Exact(name.clone()),
PathPart::Parameter { .. } => SearchablePathPart::Parameter,
};
node = node.children.entry(searchable_part).or_insert(RouteNode {
leaf: None,
children: HashMap::new(),
});
}
if node.leaf.is_some() {
return Err(RouteRegistrationError::RouteConflict);
}
node.leaf = Some(route);
Ok(())
}
pub(super) fn get_route(&self, method: &Method, path_parts: &Vec<&str>) -> Option<&Route> {
let node = match self.routes.get(method) {
Some(node) => node,
None => return None,
};
find_route(0, path_parts, node)
}
}
fn find_route<'a>(
index: usize,
parts: &Vec<&str>,
current_node: &'a RouteNode,
) -> Option<&'a Route> {
if index >= parts.len() {
return match ¤t_node.leaf {
Some(route) => Some(route),
None => None,
};
}
if let Some(exact_child) = current_node
.children
.get(&SearchablePathPart::Exact(parts[index].to_string()))
{
if let Some(route) = find_route(index + 1, parts, exact_child) {
return Some(route);
}
}
if let Some(parameter_child) = current_node.children.get(&SearchablePathPart::Parameter) {
if let Some(route) = find_route(index + 1, parts, parameter_child) {
return Some(route);
}
}
None
}
impl Route {
pub(super) fn get_parameters(&self, path_parts: &[&str]) -> HashMap<String, String> {
let mut results = HashMap::new();
for (x, path_part) in self.path.iter().enumerate() {
if let PathPart::Parameter { name } = path_part {
results.insert(name.clone(), path_parts[x].to_string());
}
}
results
}
}