#![no_std]
#![forbid(unsafe_code)]
#![warn(rust_2018_idioms, unreachable_pub)]
extern crate alloc;
use alloc::{
string::{String, ToString},
vec::Vec,
};
use core::{slice::Iter, str::from_utf8};
use smallvec::SmallVec;
mod node;
pub use node::{Key, Node};
mod parser;
pub use parser::{Kind, Parser, Piece, Position};
#[derive(Clone, Debug)]
pub struct PathTree<T> {
id: usize,
routes: Vec<(T, Vec<Piece>)>,
pub node: Node<usize>,
}
impl<T> Default for PathTree<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> PathTree<T> {
#[must_use]
pub fn new() -> Self {
Self {
id: 0,
routes: Vec::new(),
node: Node::new(Key::String(Vec::new()), None),
}
}
#[must_use]
pub fn insert(&mut self, path: &str, value: T) -> usize {
let mut node = &mut self.node;
let (overwritten, pieces) = if path.is_empty() {
(false, Vec::new())
} else {
let pieces = Parser::new(path).collect::<Vec<_>>();
node = pieces.iter().fold(node, |node, piece| match piece {
Piece::String(s) => node.insert_bytes(&s[..]),
Piece::Parameter(_, k) => node.insert_parameter(*k),
});
(true, pieces)
};
if let Some(id) = node.value {
self.routes[id].0 = value;
if overwritten {
self.routes[id].1 = pieces;
}
id
} else {
self.routes.push((value, pieces));
let id = self.id;
node.value = Some(id);
self.id += 1;
id
}
}
#[must_use]
pub fn find<'a, 'b>(&'a self, path: &'b str) -> Option<(&'a T, Path<'a, 'b>)> {
let bytes = path.as_bytes();
self.node.find(bytes).and_then(|(id, ranges)| {
self.routes.get(*id).map(|(value, pieces)| {
(
value,
Path {
id,
pieces,
raws: ranges
.into_iter()
.filter_map(|r| from_utf8(&bytes[r]).ok())
.rev()
.collect(),
},
)
})
})
}
#[must_use]
#[inline]
pub fn get_route(&self, index: usize) -> Option<&(T, Vec<Piece>)> {
self.routes.get(index)
}
#[must_use]
pub fn url_for(&self, index: usize, params: &[&str]) -> Option<String> {
self.get_route(index).and_then(|(_, pieces)| {
let mut bytes = Vec::new();
let mut iter = params.iter();
pieces.iter().for_each(|piece| match piece {
Piece::String(s) => {
bytes.extend_from_slice(s);
}
Piece::Parameter(_, _) => {
if let Some(s) = iter.next() {
bytes.extend_from_slice(s.as_bytes());
}
}
});
from_utf8(&bytes).map(ToString::to_string).ok()
})
}
pub fn iter(&self) -> Iter<'_, (T, Vec<Piece>)> {
self.routes.iter()
}
}
impl<'a, T> IntoIterator for &'a PathTree<T> {
type Item = &'a (T, Vec<Piece>);
type IntoIter = Iter<'a, (T, Vec<Piece>)>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Path<'a, 'b> {
pub id: &'a usize,
pub pieces: &'a [Piece],
pub raws: SmallVec<[&'b str; 4]>,
}
impl Path<'_, '_> {
pub fn pattern(&self) -> String {
let mut bytes = Vec::new();
self.pieces.iter().for_each(|piece| match piece {
Piece::String(s) => {
if s == b":" || s == b"+" || s == b"?" {
bytes.push(b'\\');
}
bytes.extend_from_slice(s);
}
Piece::Parameter(p, k) => match p {
Position::Index(_, _) => {
if *k == Kind::OneOrMore {
bytes.push(b'+');
} else if *k == Kind::ZeroOrMore || *k == Kind::ZeroOrMoreSegment {
bytes.push(b'*');
}
}
Position::Named(n) => match k {
Kind::Normal | Kind::Optional | Kind::OptionalSegment => {
bytes.push(b':');
bytes.extend_from_slice(n);
if *k == Kind::Optional || *k == Kind::OptionalSegment {
bytes.push(b'?');
}
}
Kind::OneOrMore => {
bytes.push(b'+');
bytes.extend_from_slice(n);
}
Kind::ZeroOrMore | Kind::ZeroOrMoreSegment => {
bytes.push(b'*');
bytes.extend_from_slice(n);
}
},
},
});
from_utf8(&bytes)
.map(ToString::to_string)
.expect("pattern generated failure")
}
#[must_use]
pub fn params(&self) -> Vec<(&str, &str)> {
self.params_iter().collect()
}
pub fn params_iter(&self) -> impl Iterator<Item = (&str, &str)> {
#[inline]
fn piece_filter(piece: &Piece) -> Option<&str> {
match piece {
Piece::String(_) => None,
Piece::Parameter(p, _) => from_utf8(match p {
Position::Index(_, n) | Position::Named(n) => n,
})
.ok(),
}
}
self.pieces
.iter()
.filter_map(piece_filter)
.zip(self.raws.iter().copied())
}
}