use crate::{
context::{HandlerMetadata, RouteId},
error::Error,
request::Request,
};
use regex::Regex;
use std::{
cmp::{min, Ordering},
collections::{HashMap, VecDeque},
sync::atomic::AtomicU64,
};
#[doc(hidden)]
static HANDLER_ID: AtomicU64 = AtomicU64::new(0);
pub(crate) enum ResolverResult<'a> {
InvalidPath,
MethodNotAllowed,
Match(&'a HandlerMetadata),
}
#[derive(Debug)]
pub(crate) enum UriPathMatcher {
Static {
segment: String,
},
Variable {
name: Option<String>,
},
Custom {
name: Option<String>,
segment: Regex,
},
Wildcard {
prefix: Option<String>,
suffix: Option<String>,
},
}
impl Eq for UriPathMatcher {}
impl PartialEq for UriPathMatcher {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == Ordering::Equal
}
}
impl PartialOrd for UriPathMatcher {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for UriPathMatcher {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.ord_index().cmp(&other.ord_index())
}
}
impl UriPathMatcher {
#[inline]
pub fn new(segment: &str) -> Result<Self, String> {
if segment.contains('/') {
return Err("A path segment should not contain any /".to_string());
}
if segment.contains('*') {
let mut segment_split = segment.splitn(2, '*');
Ok(Self::Wildcard {
prefix: segment_split
.next()
.filter(|s| !s.is_empty())
.map(|s| s.to_string()),
suffix: segment_split
.next()
.filter(|s| !s.is_empty())
.map(|s| s.to_string()),
})
} else if segment.starts_with(&['{', '<']) && segment.ends_with(&['}', '>']) {
let s: Vec<&str> = segment[1..segment.len() - 1].splitn(2, "#r").collect();
if s.is_empty() {
return Err("No name was provided for a variable segment".to_string());
}
let name = if s[0].starts_with('_') {
None
} else {
Some(s[0].to_string())
};
let name_c = name.clone();
s.get(1)
.map(|r| {
let r = r.trim_start_matches('(').trim_end_matches(')');
Regex::new(r)
.map_err(|e| e.to_string())
.map(|r| Self::Custom { name, segment: r })
})
.unwrap_or_else(|| Ok(Self::Variable { name: name_c }))
} else {
Ok(Self::Static {
segment: segment.to_string(),
})
}
}
#[inline]
fn matches(&self, other: &str) -> bool {
match self {
Self::Static { segment: ref s } => s.eq(other),
Self::Variable { .. } => true,
Self::Custom { segment: ref s, .. } => s.is_match(other),
Self::Wildcard { prefix, suffix } => {
prefix
.as_ref()
.filter(|prefix| !other.starts_with(prefix.as_str()))
.is_none()
&& suffix
.as_ref()
.filter(|suffix| !other.ends_with(suffix.as_str()))
.is_none()
}
}
}
#[inline]
fn name(&self) -> Option<&str> {
match self {
Self::Static { .. } => None,
Self::Variable { name: ref n } => n.as_ref().map(|s| s.as_str()),
Self::Custom { name: ref n, .. } => n.as_ref().map(|s| s.as_str()),
Self::Wildcard { .. } => None,
}
}
#[inline]
fn ord_index(&self) -> u16 {
match self {
Self::Static { .. } => 1,
Self::Variable { .. } => 3,
Self::Custom { .. } => 2,
Self::Wildcard { .. } => 3,
}
}
}
#[derive(Debug, Eq)]
pub(crate) enum UriPath {
Simple {
inner: Vec<UriPathMatcher>,
},
Wildcard {
name: Option<String>,
start: Vec<UriPathMatcher>,
end: VecDeque<UriPathMatcher>,
},
}
impl Ord for UriPath {
#[inline]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
let (start_self, end_self, simple_self) = match self {
Self::Simple { inner } => (inner, None, true),
Self::Wildcard { start, end, .. } => (start, Some(end), false),
};
let (start_other, end_other, simple_other) = match other {
Self::Simple { inner } => (inner, None, true),
Self::Wildcard { start, end, .. } => (start, Some(end), false),
};
let self_length = start_self.len();
let other_length = start_other.len();
let min_lenght = min(self_length, other_length);
for i in 0..min_lenght {
let cmp = start_self[i].cmp(&start_other[i]);
if cmp != Ordering::Equal {
return cmp;
}
}
if self_length > other_length {
for start in start_self.iter().take(self_length).skip(min_lenght) {
if let UriPathMatcher::Static { .. } = start {
return Ordering::Less;
}
}
}
if other_length > self_length {
for start in start_other.iter().take(other_length).skip(min_lenght) {
if let UriPathMatcher::Static { .. } = start {
return Ordering::Greater;
}
}
}
match (end_self, end_other) {
(Some(end_self), Some(end_other)) => {
let self_length = end_self.len();
let other_length = end_other.len();
let min_lenght = min(self_length, other_length);
for j in 0..min_lenght {
let cmp = end_self[j].cmp(&end_other[j]);
if cmp != Ordering::Equal {
return cmp;
}
}
other_length.cmp(&self_length)
}
(Some(e), None) if !e.is_empty() => Ordering::Less,
(None, Some(e)) if !e.is_empty() => Ordering::Greater,
_ => match (simple_self, simple_other) {
(true, false) => Ordering::Less,
(false, true) => Ordering::Greater,
_ => other_length.cmp(&self_length),
},
}
}
}
impl PartialOrd for UriPath {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for UriPath {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == Ordering::Equal
}
}
impl UriPath {
#[inline]
pub fn new(path: &str) -> Result<Self, String> {
let uri_path = if path.contains("**") || path.contains("..") {
let segments = path.split('/').collect::<Vec<_>>();
let mut name = None;
let split_at = segments
.iter()
.position(|seg| {
if seg.contains("**") || seg.contains("..") {
let trim = seg.trim_start_matches("**").trim_start_matches("..");
if !trim.is_empty() {
name = Some(trim.to_string());
}
return true;
}
false
})
.ok_or_else(|| "Unable to locate wildcard".to_string())?;
let (left, right) = segments.split_at(split_at);
let right = &right[1..right.len()];
Self::Wildcard {
name,
start: Self::parse_segments(left.iter())?,
end: Self::parse_segments(right.iter())?,
}
} else {
Self::Simple {
inner: Self::parse_segments(path.split('/'))?,
}
};
Ok(uri_path)
}
#[inline]
fn parse_segments<A, I, T>(segments: I) -> Result<T, String>
where
A: AsRef<str>,
I: Iterator<Item = A>,
T: FromIterator<UriPathMatcher>,
{
let mut err = None;
let inner = segments
.filter_map(|path| {
if path.as_ref().is_empty() {
return None;
}
match UriPathMatcher::new(path.as_ref()) {
Ok(segment) => Some(segment),
Err(e) => {
err = Some(e);
None
}
}
})
.collect::<T>();
if let Some(e) = err {
return Err(e);
}
Ok(inner)
}
#[inline]
pub(crate) fn match_non_exhaustive(&self, path: &str) -> bool {
let mut path_split = path.trim_start_matches('/').split('/').collect();
match self {
Self::Simple { inner } => Self::match_start(inner, &mut path_split),
Self::Wildcard { start, end, .. } => {
Self::match_start(start, &mut path_split) && Self::match_end(end, &mut path_split)
}
}
}
#[inline]
fn match_start(segments: &[UriPathMatcher], paths: &mut VecDeque<&str>) -> bool {
for segment in segments {
if let Some(s) = paths.pop_front() {
if !segment.matches(s) {
return false;
}
} else {
return false;
}
}
true
}
#[inline]
fn match_end(segments: &VecDeque<UriPathMatcher>, paths: &mut VecDeque<&str>) -> bool {
let mut iter = segments.iter();
while let Some(segment) = iter.next_back() {
if let Some(s) = paths.pop_back() {
if !segment.matches(s) {
return false;
}
} else {
return false;
}
}
true
}
#[inline]
pub fn match_all(&self, path: String, params: &mut HashMap<String, String>) -> bool {
let mut path_segments = path.split('/').collect::<VecDeque<_>>();
path_segments.pop_front();
if path_segments.back().map(|s| s.len()).unwrap_or(0) < 1 {
path_segments.pop_back();
}
match self {
Self::Simple { inner } => {
if inner.len() != path_segments.len() {
return false;
}
{
let mut iter = path_segments.iter();
for segment in inner.iter() {
if let Some(&s) = iter.next() {
if !segment.matches(s) {
return false;
}
} else {
return false;
}
}
}
{
for segment in inner {
if let Some(s) = path_segments.pop_front() {
if let Some(name) = segment.name() {
params.insert(name.to_string(), s.to_string());
}
}
}
}
true
}
Self::Wildcard { name, start, end } => {
let mut segments = path_segments.clone();
if Self::match_start(start, &mut segments) && Self::match_end(end, &mut segments) {
if let Some(key) = name {
let value = segments.iter().map(|&s| format!("/{}", s)).collect();
params.insert(key.clone(), value);
}
} else {
return false;
}
{
for segment in start {
if let Some(path) = path_segments.pop_front() {
if let Some(name) = segment.name() {
params.insert(name.to_string(), path.to_string());
}
}
}
let mut end_iter = end.iter();
while let Some(segment) = end_iter.next_back() {
if let Some(path) = path_segments.pop_back() {
if let Some(name) = segment.name() {
params.insert(name.to_string(), path.to_string());
}
}
}
}
true
}
}
}
}
#[derive(Debug, Eq, PartialEq)]
pub enum MethodResolver {
Specific(HashMap<&'static str, HandlerMetadata>),
Any(HandlerMetadata),
}
#[derive(Debug, Eq)]
pub struct Resolver {
id: u64,
path: UriPath,
methods: MethodResolver,
}
impl Ord for Resolver {
#[inline]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.path.cmp(&other.path)
}
}
impl PartialOrd for Resolver {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for Resolver {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == Ordering::Equal
}
}
impl Resolver {
#[inline]
pub(crate) fn new(path: &str, method: &'static str) -> Result<Self, Error> {
let id = HANDLER_ID.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
let meta = HandlerMetadata {
name: None,
route_id: RouteId::new(id),
};
let methods = if method == "ANY" {
MethodResolver::Any(meta)
} else {
let mut methods = HashMap::new();
methods.insert(method, meta);
MethodResolver::Specific(methods)
};
Ok(Self {
id,
path: UriPath::new(path).map_err(Error::Other)?,
methods,
})
}
#[inline]
pub(crate) fn new_with_metadata<T>(
path: &str,
method: &'static str,
meta: T,
) -> Result<Self, Error>
where
T: Into<Option<HandlerMetadata>>,
{
let id = HANDLER_ID.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
let mut meta = meta.into().unwrap_or_default();
meta.route_id = RouteId::new(id);
let methods = if method == "ANY" {
MethodResolver::Any(meta)
} else {
let mut methods = HashMap::new();
methods.insert(method, meta);
MethodResolver::Specific(methods)
};
Ok(Self {
id,
path: UriPath::new(path).map_err(Error::Other)?,
methods,
})
}
#[inline]
pub(crate) fn add_method(&mut self, method: &'static str) -> Result<&mut Self, Error> {
match &mut self.methods {
MethodResolver::Specific(inner) => {
if method == "ANY" {
return Err(Error::Other(String::from("Adding ANY method but an Handler already defines specific methods, This is fatal")));
}
let meta = HandlerMetadata {
name: None,
route_id: RouteId::new(self.id),
};
inner.insert(method, meta);
Ok(self)
}
MethodResolver::Any(_) => Err(Error::Other(String::from("Adding a specific endpoint method but an Handler already defines ANY method, This is fatal"))),
}
}
#[inline]
pub(crate) fn add_method_with_metadata<T>(
&mut self,
method: &'static str,
meta: T,
) -> Result<(), Error>
where
T: Into<Option<HandlerMetadata>>,
{
match &mut self.methods {
MethodResolver::Specific(inner) => {
if method == "ANY" {
return Err(Error::Other(String::from("Adding ANY method but an Handler already defines specific methods, This is fatal")));
}
let mut meta = meta.into().unwrap_or_default();
meta.route_id = RouteId::new(self.id);
inner.insert(method, meta);
Ok(())
}
MethodResolver::Any(_) => Err(Error::Other(String::from("Adding a specific endpoint method but an Handler already defines ANY method, This is fatal"))),
}
}
#[inline]
pub(crate) fn resolve(&self, req: &mut Request) -> ResolverResult {
let path = req.uri().path().to_string();
if self.path.match_all(path, req.params_mut()) {
match &self.methods {
MethodResolver::Specific(methods) => {
if let Some(meta) = methods.get(req.method().as_str()) {
ResolverResult::Match(meta)
} else {
ResolverResult::MethodNotAllowed
}
}
MethodResolver::Any(meta) => ResolverResult::Match(meta),
}
} else {
ResolverResult::InvalidPath
}
}
#[doc(hidden)]
pub(crate) fn id(&self) -> u64 {
self.id
}
}