web_route/web_route/
route.rs

1use std::{fmt, ops};
2
3use serde::{Deserialize, Deserializer, Serialize, Serializer};
4
5use crate::{to_segments::ToFixedSegments, web_route::segment::WebSegment};
6
7/// Defines a route structure that can be safely joined, no matter the
8/// leading/trailing slash configuration or operating system.
9#[derive(Clone, PartialEq)]
10pub struct WebRoute(String);
11
12impl WebRoute {
13    /// Creates a new [`WebRoute`].
14    ///
15    /// # Examples
16    ///
17    /// ```
18    /// use web_route::WebRoute;
19    ///
20    /// let route = WebRoute::new("/some/route");
21    /// ```
22    pub fn new<R: ToFixedSegments>(route: R) -> Self {
23        let segments = route.to_segments();
24
25        Self(evaluate_segments(segments))
26    }
27
28    /// Joins a route onto an existing [`WebRoute`] returning the joined
29    /// route.
30    ///
31    /// # Examples
32    ///
33    /// ```
34    /// use web_route::WebRoute;
35    ///
36    /// let route = WebRoute::new("/some/route/");
37    /// let nested_route = WebRoute::new("/a/nested/route");
38    /// let joined_route = route.join(&nested_route);
39    ///
40    /// assert_eq!(&joined_route.to_string(), "/some/route/a/nested/route")
41    /// ```
42    pub fn join<R: ToFixedSegments>(&self, route: R) -> Self {
43        let joined_segments = [self.to_segments(), route.to_segments()].concat();
44
45        Self(evaluate_segments(joined_segments))
46    }
47
48    pub(crate) fn to_segments(&self) -> Vec<WebSegment> {
49        ToFixedSegments::to_segments(&self.0)
50    }
51}
52
53impl fmt::Display for WebRoute {
54    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
55        write!(f, "{}", self.0)
56    }
57}
58
59impl fmt::Debug for WebRoute {
60    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61        f.debug_tuple("WebRoute").field(&self.to_string()).finish()
62    }
63}
64
65#[cfg(feature = "serde")]
66impl Serialize for WebRoute {
67    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
68    where
69        S: Serializer,
70    {
71        let s = self.to_string();
72        serializer.serialize_str(&s)
73    }
74}
75
76#[cfg(feature = "serde")]
77impl<'de> Deserialize<'de> for WebRoute {
78    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
79    where
80        D: Deserializer<'de>,
81    {
82        let s = String::deserialize(deserializer)?;
83        Ok(WebRoute::new(s))
84    }
85}
86
87/// Allows one to deref for usage with external crates. Makes for neater code.
88impl ops::Deref for WebRoute {
89    type Target = str;
90
91    fn deref(&self) -> &Self::Target {
92        &self.0
93    }
94}
95
96/// Convert `segments` into their normalized [`String`] route representation.
97fn evaluate_segments(segments: Vec<WebSegment>) -> String {
98    let evaluated_segments = segments
99        .iter()
100        .map(|segment| segment.to_evaluated())
101        .collect::<Vec<_>>();
102
103    format!("/{}", evaluated_segments.join("/"))
104}