use std::error::Error;
use std::fmt::{self, Display};
use crate::path::{Param, Pattern, Span};
use crate::routes::Node;
macro_rules! expect_entry {
($results:expr, $nodes:expr, $key:expr) => {
match $nodes.get($key) {
Some(descendant) => descendant,
None => {
$results.push(Err(crate::VisitError::NodeNotFound));
continue;
}
}
};
}
#[derive(Clone, Debug)]
pub enum VisitError {
NodeNotFound,
RootNotFound,
}
#[derive(Debug)]
pub struct Found {
pub is_leaf: bool,
pub route: Option<usize>,
pub param: Option<Param>,
pub at: Option<Span>,
}
pub fn visit(
results: &mut Vec<Result<Found, VisitError>>,
nodes: &[Node],
segments: &[Span],
path: &str,
) {
let root = match nodes.first() {
Some(node) => node,
None => {
results.push(Err(VisitError::NodeNotFound));
return;
}
};
match segments.first() {
Some(range) => {
results.push(Ok(Found::new(root.route, None, None)));
visit_node(results, nodes, root, path, segments, range, 0);
}
None => {
results.push(Ok(Found::leaf(root.route, None, None)));
visit_index(results, nodes, root);
}
}
}
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 Found {
fn new(route: Option<usize>, param: Option<Param>, at: Option<Span>) -> Self {
Self {
is_leaf: false,
route,
param,
at,
}
}
fn leaf(route: Option<usize>, param: Option<Param>, at: Option<Span>) -> Self {
Self {
is_leaf: true,
route,
param,
at,
}
}
}
fn visit_node(
results: &mut Vec<Result<Found, VisitError>>,
nodes: &[Node],
node: &Node,
path: &str,
segments: &[Span],
range: &Span,
index: usize,
) {
let segment = &path[range.start()..range.end()];
for key in node.entries().copied() {
let entry = expect_entry!(results, nodes, key);
match &entry.pattern {
Pattern::Static(param) if segment == param.as_str() => {
let next_index = index + 1;
let at = Some(range.clone());
match segments.get(next_index) {
Some(next_range) => {
results.push(Ok(Found::new(entry.route, None, at)));
visit_node(
results, nodes, entry, path, segments, next_range, next_index,
);
}
None => {
results.push(Ok(Found::leaf(entry.route, None, at)));
visit_index(results, nodes, entry);
}
}
}
Pattern::Dynamic(param) => {
let next_index = index + 1;
let param = Some(param.clone());
let at = Some(range.clone());
match segments.get(next_index) {
Some(next_range) => {
results.push(Ok(Found::new(entry.route, param, at)));
visit_node(
results, nodes, entry, path, segments, next_range, next_index,
);
}
None => {
results.push(Ok(Found::leaf(entry.route, param, at)));
visit_index(results, nodes, entry);
}
}
}
Pattern::Wildcard(param) => {
results.push(Ok(Found::leaf(
entry.route,
Some(param.clone()),
Some(Span::new(range.start(), path.len())),
)));
}
_ => {}
}
}
}
fn visit_index(
results: &mut Vec<Result<Found, VisitError>>,
nodes: &[Node],
node: &Node,
) {
for key in node.entries().copied() {
let entry = expect_entry!(results, nodes, key);
if let Pattern::Wildcard(param) = &entry.pattern {
results.push(Ok(Found::leaf(entry.route, Some(param.clone()), None)));
}
}
}