use std::collections::HashMap;
use hyper::Method;
use crate::handler::Handler;
#[derive(Clone, Debug, Default)]
pub struct PathParams(pub HashMap<String, String>);
#[derive(Clone, Debug, Default)]
pub struct QueryParams(pub HashMap<String, String>);
#[derive(Default)]
pub struct Router<S> {
root: Node<S>,
}
struct Node<S> {
segment: String,
param_name: String,
is_wildcard: bool,
handlers: Vec<(Method, Handler<S>)>,
children: Vec<Node<S>>,
}
impl<S> Default for Node<S> {
fn default() -> Self {
Node {
segment: String::new(),
param_name: String::new(),
is_wildcard: false,
handlers: Vec::new(),
children: Vec::new(),
}
}
}
impl<S: Send + Sync + 'static> Router<S> {
pub fn new() -> Self {
Router { root: Node::default() }
}
pub fn insert(&mut self, method: Method, path: &str, handler: Handler<S>) {
let segments = split_path(path);
let mut node = &mut self.root;
for seg in segments {
if seg == "*" {
if let Some(idx) = node.children.iter().position(|c| c.is_wildcard) {
node = &mut node.children[idx];
} else {
node.children.push(Node {
segment: "*".to_string(),
param_name: String::new(),
is_wildcard: true,
handlers: Vec::new(),
children: Vec::new(),
});
node = node.children.last_mut().unwrap();
}
} else if let Some(param_name) = seg.strip_prefix(':') {
if let Some(idx) = node.children.iter().position(|c| c.param_name == param_name) {
node = &mut node.children[idx];
} else {
node.children.push(Node {
segment: seg.to_string(),
param_name: param_name.to_string(),
is_wildcard: false,
handlers: Vec::new(),
children: Vec::new(),
});
node = node.children.last_mut().unwrap();
}
} else {
if let Some(idx) = node.children.iter().position(|c| c.segment == seg) {
node = &mut node.children[idx];
} else {
node.children.push(Node {
segment: seg.to_string(),
param_name: String::new(),
is_wildcard: false,
handlers: Vec::new(),
children: Vec::new(),
});
node = node.children.last_mut().unwrap();
}
}
}
if let Some(slot) = node.handlers.iter_mut().find(|(m, _)| *m == method) {
slot.1 = handler;
} else {
node.handlers.push((method, handler));
}
}
pub fn match_route<'a>(
&'a self,
method: &Method,
path: &str,
) -> Option<(&'a Handler<S>, PathParams)> {
let segments = split_path(path);
let mut params = PathParams::default();
let node = Self::find_node(&self.root, &segments, 0, method, &mut params)?;
node.handlers.iter().find(|(m, _)| m == method).map(|(_, h)| (h, params))
}
pub fn allowed_methods(&self, path: &str) -> Vec<Method> {
let segments = split_path(path);
let mut methods = std::collections::HashSet::new();
let mut params = PathParams::default();
Self::collect_allowed_methods(&self.root, &segments, 0, &mut params, &mut methods);
methods.into_iter().collect()
}
pub fn path_exists(&self, path: &str) -> bool {
!self.allowed_methods(path).is_empty()
}
fn find_node<'a>(
node: &'a Node<S>,
segments: &[String],
idx: usize,
method: &Method,
params: &mut PathParams,
) -> Option<&'a Node<S>> {
if idx == segments.len() {
return if node.handlers.iter().any(|(m, _)| m == method) { Some(node) } else { None };
}
let seg = &segments[idx];
for child in &node.children {
if !child.is_wildcard && child.param_name.is_empty() && child.segment == *seg {
if let Some(found) = Self::find_node(child, segments, idx + 1, method, params) {
return Some(found);
}
}
}
for child in &node.children {
if !child.is_wildcard && !child.param_name.is_empty() {
let mut p = params.clone();
p.0.insert(child.param_name.clone(), seg.clone());
if let Some(found) = Self::find_node(child, segments, idx + 1, method, &mut p) {
*params = p;
return Some(found);
}
params.0.remove(&child.param_name);
}
}
for child in &node.children {
if child.is_wildcard && child.handlers.iter().any(|(m, _)| m == method) {
params.0.insert("*".to_string(), segments[idx..].join("/"));
return Some(child);
}
}
None
}
fn collect_allowed_methods(
node: &Node<S>,
segments: &[String],
idx: usize,
params: &mut PathParams,
methods: &mut std::collections::HashSet<Method>,
) {
if idx == segments.len() {
methods.extend(node.handlers.iter().map(|(m, _)| m.clone()));
return;
}
let seg = &segments[idx];
for child in &node.children {
if !child.is_wildcard && child.param_name.is_empty() && child.segment == *seg {
let mut p = params.clone();
Self::collect_allowed_methods(child, segments, idx + 1, &mut p, methods);
}
}
for child in &node.children {
if !child.is_wildcard && !child.param_name.is_empty() {
let mut p = params.clone();
p.0.insert(child.param_name.clone(), seg.clone());
Self::collect_allowed_methods(child, segments, idx + 1, &mut p, methods);
}
}
for child in &node.children {
if child.is_wildcard {
methods.extend(child.handlers.iter().map(|(m, _)| m.clone()));
}
}
}
}
fn split_path(path: &str) -> Vec<String> {
path.trim_start_matches('/')
.split('/')
.filter(|s| !s.is_empty())
.map(|s| percent_encoding::percent_decode_str(s).decode_utf8_lossy().into_owned())
.collect()
}
#[cfg(test)]
#[path = "../tests/unit/router.rs"]
mod tests;
#[cfg(test)]
#[path = "../tests/unit/router_properties.rs"]
mod property_tests;