use crate::{
context::{Node, Router},
error::RouterError,
operations::util::{extract_all_params, normalize, split_path},
types::{MatchedRoute, MethodData, ParamEntry},
};
pub fn find_route<T: Clone + Eq>(
router: &Router<T>,
method: &str,
path: &str,
capture: bool,
) -> Result<MatchedRoute<T>, RouterError> {
let normalized_path_string = normalize(path);
if !normalized_path_string.contains([':', '*']) {
let static_map_read_guard = router.static_map.read();
if let Some(methods_for_path) = static_map_read_guard.get(&normalized_path_string) {
if let Some(method_data_list) = methods_for_path
.get(method)
.or_else(|| methods_for_path.get(""))
{
if let Some(md) = method_data_list.first() {
if md.params_map.is_none() {
return Ok(MatchedRoute {
data: md.data.clone(),
params: None,
});
}
}
}
}
}
let segments: Vec<&str> = split_path(&normalized_path_string).collect();
let root_lock = router.root.read();
match lookup_node_recursive(&*root_lock, method, &segments, 0) {
Some(md) => {
let params = if capture {
extract_all_params(&segments, &md.params_map)
} else {
None
};
Ok(MatchedRoute {
data: md.data.clone(),
params,
})
}
None => Err(RouterError::RouteNotFound {
method: method.to_string(),
path: path.to_string(),
}),
}
}
fn is_handler_for_optional_pattern<T>(md: &MethodData<T>) -> bool {
md.params_map.as_ref().is_some_and(|pm| {
pm.last().is_some_and(|p_entry| match p_entry {
ParamEntry::Index(_, _, is_opt) => *is_opt,
ParamEntry::Wildcard(_, _, is_opt) => *is_opt,
})
})
}
fn lookup_node_recursive<'a, T: Clone + Eq>(
node: &'a Node<T>,
method: &str,
segments: &[&str],
idx: usize,
) -> Option<&'a MethodData<T>> {
if idx == segments.len() {
if let Some(handlers) = node.methods.get(method).or_else(|| node.methods.get("")) {
if let Some(md) = handlers.first() {
return Some(md);
}
}
if let Some(param_child_node) = &node.param_child {
if let Some(handlers) = param_child_node
.methods
.get(method)
.or_else(|| param_child_node.methods.get(""))
{
if handlers.iter().any(is_handler_for_optional_pattern) {
if let Some(md) = handlers.first() {
return Some(md);
}
}
}
}
if let Some(wildcard_child_node) = &node.wildcard_child {
if let Some(handlers) = wildcard_child_node
.methods
.get(method)
.or_else(|| wildcard_child_node.methods.get(""))
{
if let Some(md) = handlers.first() {
return Some(md);
}
}
}
return None;
}
let current_segment_value = segments[idx];
if let Some(static_child_node) = node.static_children.get(current_segment_value) {
if let Some(found_md) = lookup_node_recursive(static_child_node, method, segments, idx + 1)
{
return Some(found_md);
}
}
if let Some(param_child_node) = &node.param_child {
if let Some(found_md) = lookup_node_recursive(param_child_node, method, segments, idx + 1) {
return Some(found_md);
}
}
if let Some(wildcard_child_node) = &node.wildcard_child {
if let Some(handlers) = wildcard_child_node
.methods
.get(method)
.or_else(|| wildcard_child_node.methods.get(""))
{
if let Some(md) = handlers.first() {
return Some(md);
}
}
}
None
}