mmids_core/http_api/
routing.rs1use async_trait::async_trait;
5use hyper::{Body, Method, Request, Response};
6use std::collections::HashMap;
7
8#[derive(Clone)]
12pub enum PathPart {
13 Exact { value: String },
15
16 Parameter { name: String },
20}
21
22#[async_trait]
26pub trait RouteHandler {
27 async fn execute(
31 &self,
32 request: &mut Request<Body>,
33 path_parameters: HashMap<String, String>,
34 request_id: String,
35 ) -> Result<Response<Body>, hyper::Error>;
36}
37
38pub struct Route {
41 pub method: Method,
42 pub path: Vec<PathPart>,
43 pub handler: Box<dyn RouteHandler + Sync + Send>,
44}
45
46#[derive(thiserror::Error, Debug)]
48pub enum RouteRegistrationError {
49 #[error("A route is already registered that conflicts with this route")]
52 RouteConflict,
53}
54
55pub struct RoutingTable {
58 routes: HashMap<Method, RouteNode>,
59}
60
61#[derive(PartialEq, Eq, Hash)]
62enum SearchablePathPart {
63 Exact(String),
64 Parameter,
65}
66
67struct RouteNode {
68 leaf: Option<Route>,
69 children: HashMap<SearchablePathPart, RouteNode>,
70}
71
72impl RoutingTable {
73 pub fn new() -> Self {
75 RoutingTable {
76 routes: HashMap::new(),
77 }
78 }
79
80 pub fn register(&mut self, route: Route) -> Result<(), RouteRegistrationError> {
82 let mut node = self
83 .routes
84 .entry(route.method.clone())
85 .or_insert(RouteNode {
86 leaf: None,
87 children: HashMap::new(),
88 });
89
90 for part in &route.path {
91 let searchable_part = match part {
92 PathPart::Exact { value: name } => SearchablePathPart::Exact(name.clone()),
93 PathPart::Parameter { .. } => SearchablePathPart::Parameter,
94 };
95
96 node = node.children.entry(searchable_part).or_insert(RouteNode {
97 leaf: None,
98 children: HashMap::new(),
99 });
100 }
101
102 if node.leaf.is_some() {
103 return Err(RouteRegistrationError::RouteConflict);
104 }
105
106 node.leaf = Some(route);
107
108 Ok(())
109 }
110
111 pub(super) fn get_route(&self, method: &Method, path_parts: &Vec<&str>) -> Option<&Route> {
112 let node = match self.routes.get(method) {
113 Some(node) => node,
114 None => return None,
115 };
116
117 find_route(0, &path_parts, node)
118 }
119}
120
121fn find_route<'a>(
122 index: usize,
123 parts: &Vec<&str>,
124 current_node: &'a RouteNode,
125) -> Option<&'a Route> {
126 if index >= parts.len() {
127 return match ¤t_node.leaf {
128 Some(route) => Some(route),
129 None => None,
130 };
131 }
132
133 if let Some(exact_child) = current_node
134 .children
135 .get(&SearchablePathPart::Exact(parts[index].to_string()))
136 {
137 if let Some(route) = find_route(index + 1, parts, exact_child) {
138 return Some(route);
139 }
140 }
141
142 if let Some(parameter_child) = current_node.children.get(&SearchablePathPart::Parameter) {
143 if let Some(route) = find_route(index + 1, parts, parameter_child) {
144 return Some(route);
145 }
146 }
147
148 None
149}
150
151impl Route {
152 pub(super) fn get_parameters(&self, path_parts: &Vec<&str>) -> HashMap<String, String> {
153 let mut results = HashMap::new();
154 for x in 0..self.path.len() {
155 if let PathPart::Parameter { name } = &self.path[x] {
156 results.insert(name.clone(), path_parts[x].to_string());
157 }
158 }
159
160 results
161 }
162}