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}