mod builder;
use anyhow::Result;
use crate::{
internal::{DataValue, END_WITH_DELIMITER},
Key, PathRegex, PathRegexOptions, TryIntoWith,
};
pub use builder::{MatcherBuilder, MatcherOptions};
#[derive(Debug)]
pub struct Matcher {
pub(crate) re: PathRegex,
pub(crate) keys: Vec<Key>,
pub(crate) options: MatcherOptions,
}
impl Matcher {
#[inline]
pub fn new<S>(path: S) -> Result<Self>
where
S: TryIntoWith<PathRegex, PathRegexOptions>,
{
MatcherBuilder::new(path).build()
}
#[inline]
pub fn new_with_options<S>(path: S, options: MatcherOptions) -> Result<Self>
where
S: TryIntoWith<PathRegex, PathRegexOptions>,
{
MatcherBuilder::new_with_options(path, options).build()
}
pub fn find<S>(&self, path: S) -> Option<MatchResult>
where
S: AsRef<str>,
{
let path = path.as_ref();
let MatcherOptions { decode, .. } = &self.options;
let captures = self.re.captures(path)?;
let m = captures.get(0)?;
let params = captures
.iter()
.skip(1)
.map(|x| x.map_or("", |x| x.as_str()))
.zip(self.keys.iter())
.map(|(value, key)| {
let Key {
name,
prefix,
suffix,
..
} = key;
if matches!(name.as_str(), "*" | "+") {
let sp = if prefix.is_empty() { suffix } else { prefix };
let value = value
.split(sp)
.map(|x| DataValue::String(decode(x, key)))
.collect();
return (name.to_owned(), DataValue::Array(value));
}
(name.to_owned(), DataValue::String(decode(value, key)))
})
.collect::<DataValue>();
let mut path = m.as_str();
if captures.name(END_WITH_DELIMITER).is_some() {
path = &path[..path.len() - 1];
}
Some(MatchResult {
index: m.start(),
path: path.to_owned(),
params,
})
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct MatchResult {
pub path: String,
pub index: usize,
pub params: DataValue,
}