use fraiseql_core::runtime::QueryMatch;
use crate::routes::rest::{
params::ExtractedParams,
resource::{HttpMethod, RestResource, RestRoute, RestRouteTable},
};
#[derive(Debug)]
pub struct ResolvedRoute<'a> {
pub resource: &'a RestResource,
pub route: &'a RestRoute,
pub path_params: Vec<(String, String)>,
}
pub struct ResolvedGetQuery {
pub query_name: String,
pub query_match: QueryMatch,
pub variables: serde_json::Value,
pub params: ExtractedParams,
}
impl RestRouteTable {
#[must_use]
pub fn resolve(&self, relative_path: &str, method: HttpMethod) -> Option<ResolvedRoute<'_>> {
let segments: Vec<&str> = relative_path
.trim_start_matches('/')
.split('/')
.filter(|s| !s.is_empty())
.collect();
for resource in &self.resources {
for route in &resource.routes {
if route.method != method {
continue;
}
if let Some(path_params) = match_route_path(&route.path, &segments) {
return Some(ResolvedRoute {
resource,
route,
path_params,
});
}
}
}
None
}
}
pub(super) fn match_route_path(
route_path: &str,
segments: &[&str],
) -> Option<Vec<(String, String)>> {
let pattern_segments: Vec<&str> = route_path
.trim_start_matches('/')
.split('/')
.filter(|s| !s.is_empty())
.collect();
if pattern_segments.len() != segments.len() {
return None;
}
let mut path_params = Vec::new();
for (pattern, actual) in pattern_segments.iter().zip(segments.iter()) {
if pattern.starts_with('{') && pattern.ends_with('}') {
let param_name = &pattern[1..pattern.len() - 1];
path_params.push((param_name.to_string(), (*actual).to_string()));
} else if *pattern != *actual {
return None;
}
}
Some(path_params)
}