web_route/
web_route.rs

1use std::fmt;
2
3use serde::{Deserialize, Deserializer, Serialize, Serializer};
4
5use crate::{
6    error::WebRouteError, segment::Segment, to_segments::ToSegments, utils::struct_to_map,
7};
8
9#[derive(Clone, PartialEq)]
10pub struct WebRoute {
11    segments: Vec<Segment>,
12}
13
14impl WebRoute {
15    /// Creates a new [`WebRoute`].
16    ///
17    /// # Examples
18    ///
19    /// ```
20    /// use web_route::WebRoute;
21    ///
22    /// let route = WebRoute::new("/some/route/{param}");
23    /// ```
24    pub fn new<R: ToSegments>(route: R) -> Self {
25        Self {
26            segments: route.to_segments(),
27        }
28    }
29
30    /// Joins a route onto an existing [`WebRoute`] returning the joined route.
31    ///
32    /// # Examples
33    ///
34    /// ```
35    /// use web_route::WebRoute;
36    ///
37    /// let route = WebRoute::new("/some/route/{param}");
38    /// let nested_route = WebRoute::new("/a/nested/route");
39    /// let joined_route = route.join(&nested_route);
40    ///
41    /// assert_eq!(joined_route, route.join("/a/nested/route"))
42    /// ```
43    pub fn join<R: ToSegments>(&self, route: R) -> Self {
44        Self {
45            segments: [self.segments.clone(), route.to_segments()].concat(),
46        }
47    }
48
49    /// Returns the route in its "templated" representation so that it can be
50    /// used in web server route definitions.
51    ///
52    /// # Examples
53    ///
54    /// ```
55    /// use web_route::WebRoute;
56    ///
57    /// let route = WebRoute::new("/some/route/{param}");
58    /// let template_route = route.as_template_route();
59    ///
60    /// assert_eq!(template_route, "/some/route/{param}")
61    /// ```
62    pub fn as_template_route(&self) -> String {
63        let template_segments = self
64            .segments
65            .iter()
66            .map(Segment::to_template)
67            .collect::<Vec<_>>();
68
69        format!("/{}", template_segments.join("/"))
70    }
71
72    /// Attempts to populate the parameters of the route with their `values`.
73    ///
74    /// `values` needs to implement `serde::Serialize` and be of an "Object"
75    /// style (with key-value pairs).
76    ///
77    /// This would be used when making a request to an endpoint represented by
78    /// the route.
79    ///
80    /// # Errors
81    ///
82    /// - [`WebRouteError::UnpopulatedParam`] if no matching entry was found in
83    ///   `values` for a particular parameter.
84    /// - [`WebRouteError::InvalidValue`] if `values` does not contain
85    ///   key-value pairs.
86    ///
87    /// # Examples
88    ///
89    /// ```
90    /// use web_route::WebRoute;
91    ///
92    /// #[derive(serde::Serialize)]
93    /// struct RouteParams {
94    ///     param: String,
95    /// }
96    ///
97    /// let route = WebRoute::new("/some/route/{param}");
98    /// let populated_route = route
99    ///     .as_populated_route(&RouteParams {
100    ///         param: "value".to_owned(),
101    ///     })
102    ///     .unwrap();
103    ///     
104    /// assert_eq!(populated_route, "/some/route/value")
105    /// ```
106    pub fn as_populated_route<V: serde::Serialize>(
107        &self,
108        values: &V,
109    ) -> Result<String, WebRouteError> {
110        let values = struct_to_map(values).ok_or(WebRouteError::InvalidValue)?;
111
112        let populated_segments = self
113            .segments
114            .iter()
115            .map(|segment| segment.to_populated(&values))
116            .collect::<Result<Vec<_>, _>>()?;
117
118        Ok(format!("/{}", populated_segments.join("/")))
119    }
120
121    pub(crate) fn segments(&self) -> Vec<Segment> {
122        self.segments.clone()
123    }
124}
125
126impl fmt::Debug for WebRoute {
127    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128        f.debug_tuple("WebRoute")
129            .field(&self.as_template_route())
130            .finish()
131    }
132}
133
134#[cfg(feature = "serde")]
135impl Serialize for WebRoute {
136    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
137    where
138        S: Serializer,
139    {
140        let s = self.as_template_route();
141        serializer.serialize_str(&s)
142    }
143}
144
145#[cfg(feature = "serde")]
146impl<'de> Deserialize<'de> for WebRoute {
147    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
148    where
149        D: Deserializer<'de>,
150    {
151        let s = String::deserialize(deserializer)?;
152        Ok(WebRoute::new(s))
153    }
154}