use super::ServerRouter;
#[cfg(feature = "viewsets")]
use super::handlers::ViewSetHandler;
use super::types::RouteHandler;
use hyper::Method;
#[cfg(feature = "viewsets")]
use reinhardt_views::viewsets::Action;
use std::borrow::Cow;
#[cfg(feature = "viewsets")]
use std::sync::Arc;
use std::sync::PoisonError;
impl ServerRouter {
pub(crate) fn compile_routes(&self) -> Vec<String> {
if *self
.routes_compiled
.read()
.unwrap_or_else(PoisonError::into_inner)
{
return Vec::new();
}
let mut errors = Vec::new();
for func_route in &self.functions {
let route_handler = RouteHandler {
handler: func_route.handler.clone(),
middleware: func_route.middleware.clone(),
};
let route_path_owned = Self::strip_prefix_normalized(&self.prefix, &func_route.path)
.unwrap_or_else(|| Cow::Borrowed(&func_route.path));
let route_path: &str = &route_path_owned;
let router_lock = match func_route.method {
Method::GET => &self.get_router,
Method::POST => &self.post_router,
Method::PUT => &self.put_router,
Method::DELETE => &self.delete_router,
Method::PATCH => &self.patch_router,
Method::HEAD => &self.head_router,
Method::OPTIONS => &self.options_router,
_ => &self.get_router,
};
if let Err(e) = router_lock
.write()
.unwrap_or_else(PoisonError::into_inner)
.insert(route_path, route_handler)
{
errors.push(format!(
"Failed to compile route '{}' ({}): {}",
func_route.path, func_route.method, e
));
}
}
for view_route in &self.views {
let route_handler = RouteHandler {
handler: view_route.handler.clone(),
middleware: view_route.middleware.clone(),
};
let route_path_owned = Self::strip_prefix_normalized(&self.prefix, &view_route.path)
.unwrap_or_else(|| Cow::Borrowed(&view_route.path));
let route_path: &str = &route_path_owned;
for router_lock in &[
&self.get_router,
&self.post_router,
&self.put_router,
&self.delete_router,
&self.patch_router,
] {
if let Err(e) = router_lock
.write()
.unwrap_or_else(PoisonError::into_inner)
.insert(route_path, route_handler.clone())
{
errors.push(format!(
"Failed to compile view route '{}': {}",
view_route.path, e
));
}
}
}
for route in &self.routes {
let route_handler = RouteHandler {
handler: route.handler_arc(),
middleware: route.middleware.clone(),
};
let route_path_owned = Self::strip_prefix_normalized(&self.prefix, &route.path)
.unwrap_or_else(|| Cow::Borrowed(&route.path));
let route_path: &str = &route_path_owned;
for router_lock in &[
&self.get_router,
&self.post_router,
&self.put_router,
&self.delete_router,
&self.patch_router,
] {
if let Err(e) = router_lock
.write()
.unwrap_or_else(PoisonError::into_inner)
.insert(route_path, route_handler.clone())
{
errors.push(format!(
"Failed to compile raw route '{}': {}",
route.path, e
));
}
}
}
#[cfg(feature = "viewsets")]
self.compile_viewset_routes(&mut errors);
*self
.routes_compiled
.write()
.unwrap_or_else(PoisonError::into_inner) = true;
errors
}
#[cfg(feature = "viewsets")]
fn compile_viewset_routes(&self, errors: &mut Vec<String>) {
for (prefix, viewset) in &self.viewsets {
let base_path = format!("/{}", prefix.trim_start_matches('/'));
let collection_path = format!("{}/", base_path.trim_end_matches('/'));
let list_handler = RouteHandler {
handler: Arc::new(ViewSetHandler {
viewset: viewset.clone(),
action: Action::list(),
}),
middleware: Vec::new(),
};
if let Err(e) = self
.get_router
.write()
.unwrap_or_else(PoisonError::into_inner)
.insert(&collection_path, list_handler)
{
errors.push(format!(
"Failed to compile ViewSet list route '{}': {}",
collection_path, e
));
}
let create_handler = RouteHandler {
handler: Arc::new(ViewSetHandler {
viewset: viewset.clone(),
action: Action::create(),
}),
middleware: Vec::new(),
};
if let Err(e) = self
.post_router
.write()
.unwrap_or_else(PoisonError::into_inner)
.insert(&collection_path, create_handler)
{
errors.push(format!(
"Failed to compile ViewSet create route '{}': {}",
collection_path, e
));
}
let lookup_field = viewset.get_lookup_field();
let detail_path = format!("{}/{{{}}}/", base_path.trim_end_matches('/'), lookup_field);
let retrieve_handler = RouteHandler {
handler: Arc::new(ViewSetHandler {
viewset: viewset.clone(),
action: Action::retrieve(),
}),
middleware: Vec::new(),
};
if let Err(e) = self
.get_router
.write()
.unwrap_or_else(PoisonError::into_inner)
.insert(&detail_path, retrieve_handler)
{
errors.push(format!(
"Failed to compile ViewSet retrieve route '{}': {}",
detail_path, e
));
}
let update_handler = RouteHandler {
handler: Arc::new(ViewSetHandler {
viewset: viewset.clone(),
action: Action::update(),
}),
middleware: Vec::new(),
};
if let Err(e) = self
.put_router
.write()
.unwrap_or_else(PoisonError::into_inner)
.insert(&detail_path, update_handler)
{
errors.push(format!(
"Failed to compile ViewSet update route '{}': {}",
detail_path, e
));
}
let destroy_handler = RouteHandler {
handler: Arc::new(ViewSetHandler {
viewset: viewset.clone(),
action: Action::destroy(),
}),
middleware: Vec::new(),
};
if let Err(e) = self
.delete_router
.write()
.unwrap_or_else(PoisonError::into_inner)
.insert(&detail_path, destroy_handler)
{
errors.push(format!(
"Failed to compile ViewSet destroy route '{}': {}",
detail_path, e
));
}
}
}
pub fn validate_routes(&self) -> std::result::Result<(), Vec<String>> {
let mut errors = self.compile_routes();
if let Err(name_errors) = self.validate_route_names() {
errors.extend(name_errors);
}
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
pub fn validate_route_names(&self) -> std::result::Result<(), Vec<String>> {
let registrations = self.collect_routes_recursive(None, "");
let mut seen: std::collections::HashMap<String, String> = std::collections::HashMap::new();
let mut errors = Vec::new();
for (name, path) in registrations {
if let Some(existing_path) = seen.insert(name.clone(), path.clone()) {
errors.push(format!(
"Duplicate route name '{}': path '{}' conflicts with existing path '{}'",
name, path, existing_path
));
}
}
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
}