use std::error::Error;
use std::fmt::{self, Display};
use crate::path::Pattern;
use crate::routes::Node;
#[derive(Clone, Debug)]
pub enum VisitError {
NodeNotFound,
RootNotFound,
}
#[derive(Debug)]
pub struct Found<'a> {
pub is_leaf: bool,
pub route: Option<usize>,
pub param: Option<&'a str>,
pub at: Option<(usize, usize)>,
}
pub fn visit<'a>(
results: &mut Vec<Result<Found<'a>, VisitError>>,
nodes: &'a [Node],
segments: &[(usize, usize)],
path: &str,
) {
let root = match nodes.first() {
Some(node) => node,
None => return results.push(Err(VisitError::RootNotFound)),
};
if let Some(range) = segments.first() {
results.push(Ok(Found::new(None, None, root.route)));
visit_node(results, nodes, root, path, segments, range, 1);
} else {
results.push(Ok(Found::leaf(None, None, root.route)));
#[rustfmt::skip]
results.extend(root.entries().filter_map(|key| match nodes.get(*key) {
Some(n @ Node { pattern: Pattern::Wildcard(param), .. }) => {
Some(Ok(Found::leaf(Some(param), None, n.route)))
}
Some(_) => None,
None => Some(Err(VisitError::NodeNotFound)),
}));
}
}
fn visit_node<'a>(
results: &mut Vec<Result<Found<'a>, VisitError>>,
nodes: &'a [Node],
node: &'a Node,
path: &str,
segments: &[(usize, usize)],
at: &(usize, usize),
index: usize,
) {
let segment = &path[at.0..at.1];
let next = segments.get(index);
for option in node.entries().map(|key| nodes.get(*key)) {
#[rustfmt::skip]
let (mut found, child) = match option {
Some(n @ Node { pattern: Pattern::Wildcard(param), .. }) => (
Found::leaf(Some(param), Some((at.0, path.len())), n.route),
n,
),
Some(n @ Node { pattern: Pattern::Dynamic(param), .. }) => (
Found::new(Some(param), Some((at.0, at.1)), n.route),
n,
),
Some(n @ Node { pattern: Pattern::Static(value), .. }) => {
if value == segment {
(Found::new(None, Some((at.0, at.1)), n.route), n)
} else {
continue;
}
}
Some(Node { pattern: Pattern::Root, .. }) => {
continue;
}
None => {
results.push(Err(VisitError::NodeNotFound));
continue;
}
};
if let Some(range) = next {
results.push(Ok(found));
visit_node(results, nodes, child, path, segments, range, index + 1);
} else {
found.is_leaf = true;
results.push(Ok(found));
#[rustfmt::skip]
results.extend(child.entries().filter_map(|key| match nodes.get(*key) {
Some(n @ Node { pattern: Pattern::Wildcard(param), .. }) => {
Some(Ok(Found::leaf(Some(param), None, n.route)))
}
Some(_) => None,
None => Some(Err(VisitError::NodeNotFound)),
}));
}
}
}
impl Display for VisitError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NodeNotFound => {
write!(f, "a node was visited that contains an invalid reference")
}
Self::RootNotFound => {
write!(f, "the route tree is missing the root node")
}
}
}
}
impl Error for VisitError {}
impl<'a> Found<'a> {
#[inline]
fn new(param: Option<&'a str>, at: Option<(usize, usize)>, route: Option<usize>) -> Self {
Self {
is_leaf: false,
route,
param,
at,
}
}
#[inline]
fn leaf(param: Option<&'a str>, at: Option<(usize, usize)>, route: Option<usize>) -> Self {
Self {
is_leaf: true,
route,
param,
at,
}
}
}