web_route/parameterized_route/
route.rs

1use std::fmt;
2
3use crate::{
4    WebRoute, error::WebRouteError, parameterized_route::segment::ParameterizedSegment,
5    to_segments::ToParameterizedSegments, utils::struct_to_map,
6};
7
8/// Defines a route structure that can be used to define routes for a webserver.
9///
10/// Its templated sections can be easily populated to create a [`WebRoute`]
11/// which can be used to make requests against the webserver routes that the
12/// [`ParameterizedRoute`] was used to define.
13#[derive(Clone, PartialEq)]
14pub struct ParameterizedRoute {
15    segments: Vec<ParameterizedSegment>,
16}
17
18impl ParameterizedRoute {
19    /// Creates a new [`ParameterizedRoute`].
20    ///
21    /// # Examples
22    ///
23    /// ```
24    /// use web_route::ParameterizedRoute;
25    ///
26    /// let route = ParameterizedRoute::new("/some/route/{param}");
27    /// ```
28    pub fn new<R: ToParameterizedSegments>(route: R) -> Self {
29        Self {
30            segments: route.to_segments(),
31        }
32    }
33
34    /// Joins a route onto an existing [`ParameterizedRoute`] returning the
35    /// joined route.
36    ///
37    /// # Examples
38    ///
39    /// ```
40    /// use web_route::ParameterizedRoute;
41    ///
42    /// let route = ParameterizedRoute::new("/some/route/{param}");
43    /// let nested_route = ParameterizedRoute::new("/a/nested/route");
44    /// let joined_route = route.join(&nested_route);
45    ///
46    /// assert_eq!(joined_route, route.join("/a/nested/route"))
47    /// ```
48    pub fn join<R: ToParameterizedSegments>(&self, route: R) -> Self {
49        Self {
50            segments: [self.segments.clone(), route.to_segments()].concat(),
51        }
52    }
53
54    /// Attempts to populate the parameters of the route with their `values` and
55    /// returns a [`WebRoute`].
56    ///
57    /// `values` needs to implement `serde::Serialize` and be of an "Object"
58    /// style (with key-value pairs).
59    ///
60    /// This would be used when making a request to an endpoint represented by
61    /// the route.
62    ///
63    /// # Errors
64    ///
65    /// - [`WebRouteError::UnpopulatedParam`] if no matching entry was found in
66    ///   `values` for a particular parameter.
67    /// - [`WebRouteError::InvalidValue`] if `values` does not contain key-value
68    ///   pairs.
69    ///
70    /// # Examples
71    ///
72    /// ```
73    /// use web_route::ParameterizedRoute;
74    ///
75    /// #[derive(serde::Serialize)]
76    /// struct RouteParams {
77    ///     param: String,
78    /// }
79    ///
80    /// let parameterized_route = ParameterizedRoute::new("/some/route/{param}");
81    /// let web_route = parameterized_route
82    ///     .to_web_route(&RouteParams {
83    ///         param: "value".to_owned(),
84    ///     })
85    ///     .unwrap();
86    ///     
87    /// assert_eq!(&web_route.to_string(), "/some/route/value")
88    /// ```
89    pub fn to_web_route<V: serde::Serialize>(&self, values: &V) -> Result<WebRoute, WebRouteError> {
90        let values = struct_to_map(values).ok_or(WebRouteError::InvalidValue)?;
91
92        let populated_segments = self
93            .segments
94            .iter()
95            .map(|segment| segment.to_populated(&values))
96            .collect::<Result<Vec<_>, _>>()?;
97
98        let web_route = WebRoute::new(format!("/{}", populated_segments.join("/")));
99
100        Ok(web_route)
101    }
102
103    pub(crate) fn segments(&self) -> Vec<ParameterizedSegment> {
104        self.segments.clone()
105    }
106}
107
108impl fmt::Display for ParameterizedRoute {
109    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
110        let template_segments = self
111            .segments
112            .iter()
113            .map(ParameterizedSegment::to_template)
114            .collect::<Vec<_>>();
115
116        write!(f, "/{}", template_segments.join("/"))
117    }
118}
119
120impl fmt::Debug for ParameterizedRoute {
121    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122        f.debug_tuple("ParameterizedRoute")
123            .field(&self.to_string())
124            .finish()
125    }
126}