use super::path_pattern::PathPattern;
use super::radix::{RadixRouter, RadixRouterError};
use super::validation::validate_path_param;
use reinhardt_http::PathParams;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MatchingMode {
Linear,
RadixTree,
}
pub struct PathMatcher {
patterns: Vec<(PathPattern, String)>, radix_router: Option<RadixRouter>,
mode: MatchingMode,
}
impl PathMatcher {
pub fn new() -> Self {
Self {
patterns: Vec::new(),
radix_router: None,
mode: MatchingMode::Linear,
}
}
pub fn with_mode(mode: MatchingMode) -> Self {
Self {
patterns: Vec::new(),
radix_router: if mode == MatchingMode::RadixTree {
Some(RadixRouter::new())
} else {
None
},
mode,
}
}
pub fn enable_radix_tree(&mut self) -> Result<(), RadixRouterError> {
if self.mode == MatchingMode::RadixTree {
return Ok(()); }
let mut radix_router = RadixRouter::new();
for (pattern, handler_id) in &self.patterns {
radix_router.add_route(&pattern.to_matchit_pattern(), handler_id.clone())?;
}
self.mode = MatchingMode::RadixTree;
self.radix_router = Some(radix_router);
Ok(())
}
pub fn mode(&self) -> MatchingMode {
self.mode
}
pub fn add_pattern(
&mut self,
pattern: PathPattern,
handler_id: String,
) -> Result<(), RadixRouterError> {
let matchit_pattern = pattern.to_matchit_pattern();
if let Some(ref mut radix_router) = self.radix_router {
radix_router.add_route(&matchit_pattern, handler_id.clone())?;
}
self.patterns.push((pattern, handler_id));
Ok(())
}
pub fn match_path(&self, path: &str) -> Option<(String, PathParams)> {
match self.mode {
MatchingMode::RadixTree => {
if let Some(ref radix_router) = self.radix_router {
let (handler_id, params) = radix_router.match_path(path)?;
if let Some((pattern, _)) =
self.patterns.iter().find(|(_, id)| *id == handler_id)
{
for (name, value) in params.iter() {
if pattern.path_type_params.contains(name)
&& !validate_path_param(value)
{
return None;
}
}
}
Some((handler_id, params))
} else {
self.match_path_linear(path)
}
}
MatchingMode::Linear => {
self.match_path_linear(path)
}
}
}
fn match_path_linear(&self, path: &str) -> Option<(String, PathParams)> {
'outer: for (pattern, handler_id) in &self.patterns {
if let Some(captures) = pattern.regex.captures(path) {
let mut params = PathParams::new();
for name in pattern.param_names() {
if let Some(value) = captures.name(name) {
let val = value.as_str();
if pattern.path_type_params.contains(name) && !validate_path_param(val) {
continue 'outer;
}
params.insert(name.clone(), val.to_string());
}
}
return Some((handler_id.clone(), params));
}
}
None
}
}
impl Default for PathMatcher {
fn default() -> Self {
Self::new()
}
}