use super::defs::*;
use std::str::from_utf8;
use std::str::from_utf8_unchecked;
#[derive(Clone)]
pub enum RadixRule {
Plain {
frag: Bytes
},
Param {
frag: Bytes,
name: Bytes,
},
Glob {
frag: Bytes,
glob: glob::Pattern
},
Regex {
frag: Bytes,
name: Bytes,
expr: Regex,
},
}
impl RadixRule {
#[inline]
pub fn from_plain(frag: impl Into<Bytes>) -> RadixResult<Self> {
Ok(Self::Plain { frag: frag.into() })
}
#[inline]
pub fn from_param(frag: impl Into<Bytes>) -> RadixResult<Self> {
let frag = frag.into();
if !frag.starts_with(b":") {
return Err(RadixError::PathMalformed("param lack of colon"));
}
let name = frag.slice(1..);
Ok(Self::Param { frag, name })
}
#[inline]
pub fn from_glob(frag: impl Into<Bytes>) -> RadixResult<Self> {
let frag = frag.into();
if !frag.starts_with(b"*") {
return Err(RadixError::PathMalformed("glob lack of asterisk"));
}
let glob = glob::Pattern::new(from_utf8(frag.as_ref())?)?;
Ok(Self::Glob { frag, glob })
}
#[inline]
pub fn from_regex(frag: impl Into<Bytes>) -> RadixResult<Self> {
let frag = frag.into();
if !frag.starts_with(b"{") || !frag.ends_with(b"}") {
return Err(RadixError::PathMalformed("regex lack of curly braces"));
}
let data = frag.slice(1..frag.len() - 1);
let find = match memchr::memchr(b':', data.as_ref()) {
Some(pos) => (data.slice(..pos), from_utf8(&data[pos + 1..])?),
None => (Bytes::new(), from_utf8(data.as_ref())?)
};
let (name, expr) = match find.1.as_bytes().first() {
Some(b'^') => (find.0, Regex::new(find.1)?),
_ => (find.0, Regex::new(('^'.to_string() + find.1).as_str())?)
};
Ok(Self::Regex { frag, name, expr })
}
#[inline]
pub fn is_plain(&self) -> bool {
matches!(self, RadixRule::Plain { .. })
}
#[inline]
pub fn is_special(&self) -> bool {
!self.is_plain()
}
#[inline]
pub fn longest<'u>(&self, path: &'u [u8], raw: bool) -> Option<&'u [u8]> {
if matches!(self, RadixRule::Plain { .. }) || raw {
let frag = match self {
RadixRule::Plain { frag, .. } => frag,
RadixRule::Param { frag, .. } => frag,
RadixRule::Glob { frag, .. } => frag,
RadixRule::Regex { frag, .. } => frag,
};
let min = std::cmp::min(frag.len(), path.len());
let mut len = 0;
const BLK: usize = std::mem::size_of::<usize>();
while len + BLK <= min {
let frag_chunk: &usize = unsafe { &*(frag.as_ptr().add(len) as *const usize) };
let path_chunk: &usize = unsafe { &*(path.as_ptr().add(len) as *const usize) };
match frag_chunk == path_chunk {
true => len += BLK,
false => break,
}
}
while len < min && frag[len] == path[len] {
len += 1;
}
return Some(&path[..len]);
}
match self {
RadixRule::Param { .. } => match memchr::memchr(b'/', path) {
Some(p) => Some(&path[..p]),
None if !path.is_empty() => Some(path),
None => None
}
RadixRule::Glob { glob, .. } => {
let utf8 = match from_utf8(path) {
Ok(p) => p,
Err(_) => return None,
};
match glob.matches(utf8) {
true => Some(path),
false => None
}
}
RadixRule::Regex { expr, .. } => {
let utf8 = match from_utf8(path) {
Ok(p) => p,
Err(_) => return None,
};
match expr.find(utf8) {
Some(m) => Some(&path[..m.len()]),
None => None
}
}
RadixRule::Plain { .. } => unreachable!(),
}
}
#[inline]
pub fn divide(&mut self, len: usize) -> RadixResult<RadixRule> {
match self {
RadixRule::Plain { frag } if frag.len() > len => {
let rule = RadixRule::from_plain(frag.slice(len..));
*frag = frag.slice(..len);
rule
}
_ => Err(RadixError::RuleIndivisible)
}
}
#[inline]
pub fn origin(&self) -> &Bytes {
match self {
RadixRule::Plain { frag } => frag,
RadixRule::Param { frag, .. } => frag,
RadixRule::Glob { frag, .. } => frag,
RadixRule::Regex { frag, .. } => frag,
}
}
#[inline]
pub fn identity(&self) -> &Bytes {
static EMPTY: Bytes = Bytes::new();
static GLOB: Bytes = Bytes::from_static(b"*");
match self {
RadixRule::Plain { .. } => &EMPTY,
RadixRule::Param { name, .. } => name,
RadixRule::Glob { .. } => &GLOB,
RadixRule::Regex { name, .. } => name,
}
}
}
impl TryFrom<Bytes> for RadixRule {
type Error = RadixError;
fn try_from(path: Bytes) -> Result<Self, Self::Error> {
let init = path.first().ok_or(RadixError::PathEmpty)?;
match *init {
b':' => match memchr::memchr(b'/', path.as_ref()) {
Some(pos) => Self::from_param(path.slice(..pos)),
_ => Self::from_param(path),
}
b'*' => {
Self::from_glob(path)
}
b'{' => match memchr::memchr(b'}', path.as_ref()) {
Some(pos) => Self::from_regex(path.slice(..pos + 1)),
_ => Err(RadixError::PathMalformed("missing closing sign '}'"))
}
_ => match memchr::memchr3(b'{', b':', b'*', path.as_ref()) {
Some(pos) => Self::from_plain(path.slice(..pos)),
None => Self::from_plain(path),
}
}
}
}
impl TryFrom<&'static [u8]> for RadixRule {
type Error = RadixError;
fn try_from(path: &'static [u8]) -> Result<Self, Self::Error> {
Bytes::from(path).try_into()
}
}
impl TryFrom<&'static str> for RadixRule {
type Error = RadixError;
fn try_from(path: &'static str) -> Result<Self, Self::Error> {
Bytes::from(path).try_into()
}
}
impl Default for RadixRule {
#[inline]
fn default() -> Self {
Self::Plain { frag: Bytes::new() }
}
}
impl Debug for RadixRule {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let (kind, frag) = match self {
RadixRule::Plain { frag } => ("Plain", frag),
RadixRule::Param { frag, .. } => ("Param", frag),
RadixRule::Glob { frag, .. } => ("Glob", frag),
RadixRule::Regex { frag, .. } => ("Regex", frag),
};
write!(f, "{}({})", kind, unsafe { from_utf8_unchecked(frag.as_ref()) })
}
}
impl Hash for RadixRule {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
RadixRule::Plain { frag } => {
"Plain".hash(state);
frag.hash(state);
}
RadixRule::Param { frag, .. } => {
"Param".hash(state);
frag.hash(state);
}
RadixRule::Glob { frag, .. } => {
"Glob".hash(state);
frag.hash(state);
}
RadixRule::Regex { frag, .. } => {
"Regex".hash(state);
frag.hash(state);
}
}
}
}
impl Eq for RadixRule {}
impl PartialEq for RadixRule {
#[inline]
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(RadixRule::Plain { frag: a }, RadixRule::Plain { frag: b }) => a == b,
(RadixRule::Param { frag: a, .. }, RadixRule::Param { frag: b, .. }) => a == b,
(RadixRule::Glob { frag: a, .. }, RadixRule::Glob { frag: b, .. }) => a == b,
(RadixRule::Regex { frag: a, .. }, RadixRule::Regex { frag: b, .. }) => a == b,
_ => false
}
}
}
impl PartialEq<&[u8]> for RadixRule {
#[inline]
fn eq(&self, other: &&[u8]) -> bool {
self.origin() == *other
}
}
impl<const N: usize> PartialEq<&[u8; N]> for RadixRule {
#[inline]
fn eq(&self, other: &&[u8; N]) -> bool {
self.origin() == other.as_ref()
}
}
impl PartialEq<&str> for RadixRule {
#[inline]
fn eq(&self, other: &&str) -> bool {
self.origin() == other.as_bytes()
}
}