use crate::capture::Captures;
use crate::matcher::Matcher;
use crate::node::Node;
use crate::parser::Parser;
pub struct Router<T> {
root: Node<T>,
parsers: Vec<Box<dyn Parser>>,
}
impl<T> Router<T> {
pub fn new(mut parsers: Vec<Box<dyn Parser>>) -> Self {
parsers.shrink_to_fit();
let parsed = parse_segment(&parsers, "/");
let parsed = parsed.expect("unparsed segment");
Self {
parsers,
root: Node::new(parsed),
}
}
#[inline(always)]
pub fn insert(&mut self, path: &str, t: T) {
self.update(path, |_| t)
}
pub fn lookup<'a>(&'a self, path: &str) -> Option<(&T, Captures<'a>)> {
let offset = path.as_ptr() as usize;
let mut current = &self.root;
let mut captures = Vec::new();
for segment in path.split('/').filter(|s| !s.is_empty()) {
current = current
.children()
.iter()
.find(|child| child.matcher().is_match(segment))?;
let matcher = current.matcher();
let capture = matcher.capture(segment);
if let Some((name, (start, end))) = capture {
let ptr = segment.as_ptr() as usize - offset;
let val = (ptr + start, ptr + end);
captures.push((name, val));
}
}
current.value().map(|handler| (handler, captures))
}
pub fn update<F>(&mut self, path: &str, f: F)
where
F: FnOnce(Option<T>) -> T,
{
let mut current = &mut self.root;
for segment in path.split('/').filter(|s| !s.is_empty()) {
let child = current
.children()
.iter()
.find(|child| child.matcher().is_match(segment));
if child.is_none() {
let parsed = parse_segment(&self.parsers, segment);
let parsed = parsed.expect("unparsed segment");
let router = Node::new(parsed);
current.add_child(router);
}
current = current
.children_mut()
.iter_mut()
.find(|child| child.matcher().is_match(segment))
.unwrap();
}
current.update(f);
}
}
fn parse_segment(parsers: &[Box<dyn Parser>], segment: &str) -> Option<Box<dyn Matcher>> {
parsers.iter().find_map(|parser| parser.parse(segment))
}