use std::{collections::HashMap, sync::Arc};
use uuid::Uuid;
use super::path_error::PathError;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub struct RouteId(Uuid);
impl From<Uuid> for RouteId {
fn from(uuid: Uuid) -> Self {
Self(uuid)
}
}
#[derive(Clone, Default, Debug)]
pub struct Routes {
router: path_tree::PathTree<RouteId>,
paths_by_id: HashMap<RouteId, Arc<str>>,
ids_by_path: HashMap<Arc<str>, RouteId>,
}
impl Routes {
pub fn at<'route, 'path>(
&'route self,
path: &'path str,
) -> Option<(&'route RouteId, path_tree::Path<'route, 'path>)> {
self.router.find(path)
}
pub fn insert(&mut self, route: impl Into<String>, value: RouteId) {
let route = route.into();
let _index = self.router.insert(route.as_str(), value);
let shared_path: Arc<str> = route.into();
self.paths_by_id.insert(value, shared_path.clone());
self.ids_by_path.insert(shared_path, value);
}
pub fn get_id(&self, path: &str) -> Option<RouteId> {
self.ids_by_path.get(path).copied()
}
pub fn get_path(&self, id: RouteId) -> Option<&str> {
self.paths_by_id.get(&id).map(|path| path.as_ref())
}
pub fn capture<T>(&self, path: impl AsRef<str>) -> Result<T, PathError>
where
T: serde::de::DeserializeOwned,
{
let Some((_route_id, captures)) = self.router.find(path.as_ref()) else {
return Err(PathError::FailedToCapture(path.as_ref().to_string()));
};
let mut raws = Vec::new();
for value in &captures.raws {
raws.push(value);
}
let slice = serde_json::to_vec(&raws)?;
Ok(serde_json::from_slice(&slice)?)
}
pub fn paths(&self) -> Vec<String> {
let mut routes: Vec<String> = self
.ids_by_path
.keys()
.map(|path| path.to_string())
.collect();
routes.sort();
routes
}
}
#[test]
fn grabbing_path_captures() -> Result<(), tower::BoxError> {
let mut routes = Routes::default();
let route_id = RouteId::from(uuid::Uuid::new_v4());
routes.insert("/view/:id/children/:child_id/add/:new_child_name", route_id);
let uuid_one = uuid::Uuid::new_v4();
let uuid_two = uuid::Uuid::new_v4();
let uuid_three = uuid::Uuid::new_v4();
let path = format!("/view/{uuid_one}/children/{uuid_two}/add/{uuid_three}");
let uuids: (uuid::Uuid, uuid::Uuid, uuid::Uuid) = routes.capture(path)?;
assert_eq!(uuids, (uuid_one, uuid_two, uuid_three));
Ok(())
}