use crate::ParamsMap;
#[derive(Debug, Clone, PartialEq, Eq)]
#[doc(hidden)]
pub struct PathMatch {
pub path: String,
pub params: ParamsMap,
}
#[doc(hidden)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Matcher {
splat: Option<String>,
segments: Vec<String>,
len: usize,
partial: bool,
}
impl Matcher {
#[doc(hidden)]
pub fn new(path: &str) -> Self {
Self::new_with_partial(path, false)
}
#[doc(hidden)]
pub fn new_with_partial(path: &str, partial: bool) -> Self {
let (pattern, splat) = match path.split_once("/*") {
Some((p, s)) => (p, Some(s.to_string())),
None => (path, None),
};
let segments = pattern
.split('/')
.filter(|n| !n.is_empty())
.map(|n| n.to_string())
.collect::<Vec<_>>();
let len = segments.len();
Self {
splat,
segments,
len,
partial,
}
}
#[doc(hidden)]
pub fn test(&self, location: &str) -> Option<PathMatch> {
let loc_segments = location
.split('/')
.filter(|n| !n.is_empty())
.collect::<Vec<_>>();
let loc_len = loc_segments.len();
let len_diff: i32 = loc_len as i32 - self.len as i32;
if loc_len < self.len
|| (len_diff > 0 && self.splat.is_none() && !self.partial)
{
None
}
else {
let mut path = String::new();
let mut params = ParamsMap::new();
for (segment, loc_segment) in
self.segments.iter().zip(loc_segments.iter())
{
if let Some(param_name) = segment.strip_prefix(':') {
params.insert(param_name.into(), (*loc_segment).into());
} else if segment != loc_segment {
return None;
}
path.push('/');
path.push_str(loc_segment);
}
if let Some(splat) = &self.splat {
if !splat.is_empty() {
let value = if len_diff > 0 {
loc_segments[self.len..].join("/")
} else {
"".into()
};
params.insert(splat.into(), value);
}
}
Some(PathMatch { path, params })
}
}
}