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}