use std::{fmt, ops};
use crate::{
WebRoute, error::WebRouteError, parameterized_route::segment::ParameterizedSegment,
to_segments::ToParameterizedSegments, utils::struct_to_map,
};
#[derive(Clone, PartialEq)]
pub struct ParameterizedRoute(String);
impl ParameterizedRoute {
pub fn new<R: ToParameterizedSegments>(route: R) -> Self {
let segments = route.to_segments();
Self(evaluate_segments(segments))
}
pub fn join<R: ToParameterizedSegments>(&self, route: R) -> Self {
let joined_segments = [self.to_segments(), route.to_segments()].concat();
Self(evaluate_segments(joined_segments))
}
pub fn to_web_route<V: serde::Serialize>(&self, values: &V) -> Result<WebRoute, WebRouteError> {
let values = struct_to_map(values).ok_or(WebRouteError::InvalidValue)?;
let populated_segments = self
.to_segments()
.iter()
.map(|segment| segment.to_populated(&values))
.collect::<Result<Vec<_>, _>>()?;
let web_route = WebRoute::new(format!("/{}", populated_segments.join("/")));
Ok(web_route)
}
pub(crate) fn to_segments(&self) -> Vec<ParameterizedSegment> {
ToParameterizedSegments::to_segments(&self.0)
}
}
impl fmt::Display for ParameterizedRoute {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl fmt::Debug for ParameterizedRoute {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("ParameterizedRoute")
.field(&self.to_string())
.finish()
}
}
impl ops::Deref for ParameterizedRoute {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl AsRef<str> for ParameterizedRoute {
fn as_ref(&self) -> &str {
&self.0
}
}
#[cfg(feature = "fake")]
impl fake::Dummy<fake::Faker> for ParameterizedRoute {
fn dummy_with_rng<R: fake::RngExt + ?Sized>(config: &fake::Faker, rng: &mut R) -> Self {
use fake::Fake;
let segments: Vec<ParameterizedSegment> = config.fake_with_rng(rng);
Self::new(segments)
}
}
fn evaluate_segments(segments: Vec<ParameterizedSegment>) -> String {
let evaluated_segments = segments
.iter()
.map(ParameterizedSegment::to_template)
.collect::<Vec<_>>();
format!("/{}", evaluated_segments.join("/"))
}
#[cfg(test)]
mod parameterized_route_tests {
use super::*;
mod to_web_route {
use std::ops::Deref;
use super::*;
#[test]
fn should_normalize_double_forward_slashes() {
#[derive(serde::Serialize)]
struct RouteParams {
param: String,
}
let parameterized_route = ParameterizedRoute::new("/some/route/{param}");
let web_route = parameterized_route
.to_web_route(&RouteParams {
param: "/value".to_owned(),
})
.unwrap();
assert_eq!(web_route.deref(), "/some/route/value")
}
}
}