/src/http/src/uri/builder.rs
Line | Count | Source |
1 | | use std::convert::TryInto; |
2 | | |
3 | | use super::{Authority, Parts, PathAndQuery, Scheme}; |
4 | | use crate::Uri; |
5 | | |
6 | | /// A builder for `Uri`s. |
7 | | /// |
8 | | /// This type can be used to construct an instance of `Uri` |
9 | | /// through a builder pattern. |
10 | | #[derive(Debug)] |
11 | | pub struct Builder { |
12 | | parts: Result<Parts, crate::Error>, |
13 | | } |
14 | | |
15 | | impl Builder { |
16 | | /// Creates a new default instance of `Builder` to construct a `Uri`. |
17 | | /// |
18 | | /// # Examples |
19 | | /// |
20 | | /// ``` |
21 | | /// # use http::*; |
22 | | /// |
23 | | /// let uri = uri::Builder::new() |
24 | | /// .scheme("https") |
25 | | /// .authority("hyper.rs") |
26 | | /// .path_and_query("/") |
27 | | /// .build() |
28 | | /// .unwrap(); |
29 | | /// ``` |
30 | | #[inline] |
31 | 0 | pub fn new() -> Builder { |
32 | 0 | Builder::default() |
33 | 0 | } |
34 | | |
35 | | /// Set the `Scheme` for this URI. |
36 | | /// |
37 | | /// # Examples |
38 | | /// |
39 | | /// ``` |
40 | | /// # use http::*; |
41 | | /// |
42 | | /// let mut builder = uri::Builder::new(); |
43 | | /// builder.scheme("https"); |
44 | | /// ``` |
45 | | pub fn scheme<T>(self, scheme: T) -> Self |
46 | | where |
47 | | T: TryInto<Scheme>, |
48 | | <T as TryInto<Scheme>>::Error: Into<crate::Error>, |
49 | | { |
50 | | self.map(move |mut parts| { |
51 | | let scheme = scheme.try_into().map_err(Into::into)?; |
52 | | parts.scheme = Some(scheme); |
53 | | Ok(parts) |
54 | | }) |
55 | | } |
56 | | |
57 | | /// Set the `Authority` for this URI. |
58 | | /// |
59 | | /// # Examples |
60 | | /// |
61 | | /// ``` |
62 | | /// # use http::*; |
63 | | /// |
64 | | /// let uri = uri::Builder::new() |
65 | | /// .authority("tokio.rs") |
66 | | /// .build() |
67 | | /// .unwrap(); |
68 | | /// ``` |
69 | | pub fn authority<T>(self, auth: T) -> Self |
70 | | where |
71 | | T: TryInto<Authority>, |
72 | | <T as TryInto<Authority>>::Error: Into<crate::Error>, |
73 | | { |
74 | | self.map(move |mut parts| { |
75 | | let auth = auth.try_into().map_err(Into::into)?; |
76 | | parts.authority = Some(auth); |
77 | | Ok(parts) |
78 | | }) |
79 | | } |
80 | | |
81 | | /// Set the `PathAndQuery` for this URI. |
82 | | /// |
83 | | /// # Examples |
84 | | /// |
85 | | /// ``` |
86 | | /// # use http::*; |
87 | | /// |
88 | | /// let uri = uri::Builder::new() |
89 | | /// .path_and_query("/hello?foo=bar") |
90 | | /// .build() |
91 | | /// .unwrap(); |
92 | | /// ``` |
93 | | pub fn path_and_query<T>(self, p_and_q: T) -> Self |
94 | | where |
95 | | T: TryInto<PathAndQuery>, |
96 | | <T as TryInto<PathAndQuery>>::Error: Into<crate::Error>, |
97 | | { |
98 | | self.map(move |mut parts| { |
99 | | let p_and_q = match p_and_q.try_into() { |
100 | | Ok(p_and_q) => p_and_q, |
101 | | Err(err) => { |
102 | | let err = err.into(); |
103 | | if err.is_empty_uri() { |
104 | | PathAndQuery::empty() |
105 | | } else { |
106 | | return Err(err); |
107 | | } |
108 | | } |
109 | | }; |
110 | | parts.path_and_query = Some(p_and_q); |
111 | | Ok(parts) |
112 | | }) |
113 | | } |
114 | | |
115 | | /// Consumes this builder, and tries to construct a valid `Uri` from |
116 | | /// the configured pieces. |
117 | | /// |
118 | | /// # Errors |
119 | | /// |
120 | | /// This function may return an error if any previously configured argument |
121 | | /// failed to parse or get converted to the internal representation. For |
122 | | /// example if an invalid `scheme` was specified via `scheme("!@#%/^")` |
123 | | /// the error will be returned when this function is called rather than |
124 | | /// when `scheme` was called. |
125 | | /// |
126 | | /// Additionally, the various forms of URI require certain combinations of |
127 | | /// parts to be set to be valid. If the parts don't fit into any of the |
128 | | /// valid forms of URI, a new error is returned. |
129 | | /// |
130 | | /// # Examples |
131 | | /// |
132 | | /// ``` |
133 | | /// # use http::*; |
134 | | /// |
135 | | /// let uri = Uri::builder() |
136 | | /// .build() |
137 | | /// .unwrap(); |
138 | | /// ``` |
139 | 0 | pub fn build(self) -> Result<Uri, crate::Error> { |
140 | 0 | let parts = self.parts?; |
141 | 0 | Uri::from_parts(parts).map_err(Into::into) |
142 | 0 | } |
143 | | |
144 | | // private |
145 | | |
146 | | fn map<F>(self, func: F) -> Self |
147 | | where |
148 | | F: FnOnce(Parts) -> Result<Parts, crate::Error>, |
149 | | { |
150 | | Builder { |
151 | | parts: self.parts.and_then(func), |
152 | | } |
153 | | } |
154 | | } |
155 | | |
156 | | impl Default for Builder { |
157 | | #[inline] |
158 | 0 | fn default() -> Builder { |
159 | 0 | Builder { |
160 | 0 | parts: Ok(Parts::default()), |
161 | 0 | } |
162 | 0 | } |
163 | | } |
164 | | |
165 | | impl From<Uri> for Builder { |
166 | 0 | fn from(uri: Uri) -> Self { |
167 | 0 | Self { |
168 | 0 | parts: Ok(uri.into_parts()), |
169 | 0 | } |
170 | 0 | } |
171 | | } |
172 | | |
173 | | #[cfg(test)] |
174 | | mod tests { |
175 | | use super::*; |
176 | | |
177 | | #[test] |
178 | | fn build_from_str() { |
179 | | let uri = Builder::new() |
180 | | .scheme(Scheme::HTTP) |
181 | | .authority("hyper.rs") |
182 | | .path_and_query("/foo?a=1") |
183 | | .build() |
184 | | .unwrap(); |
185 | | assert_eq!(uri.scheme_str(), Some("http")); |
186 | | assert_eq!(uri.authority().unwrap().host(), "hyper.rs"); |
187 | | assert_eq!(uri.path(), "/foo"); |
188 | | assert_eq!(uri.query(), Some("a=1")); |
189 | | } |
190 | | |
191 | | #[test] |
192 | | fn build_from_string() { |
193 | | for i in 1..10 { |
194 | | let uri = Builder::new() |
195 | | .path_and_query(format!("/foo?a={}", i)) |
196 | | .build() |
197 | | .unwrap(); |
198 | | let expected_query = format!("a={}", i); |
199 | | assert_eq!(uri.path(), "/foo"); |
200 | | assert_eq!(uri.query(), Some(expected_query.as_str())); |
201 | | } |
202 | | } |
203 | | |
204 | | #[test] |
205 | | fn build_from_string_ref() { |
206 | | for i in 1..10 { |
207 | | let p_a_q = format!("/foo?a={}", i); |
208 | | let uri = Builder::new().path_and_query(&p_a_q).build().unwrap(); |
209 | | let expected_query = format!("a={}", i); |
210 | | assert_eq!(uri.path(), "/foo"); |
211 | | assert_eq!(uri.query(), Some(expected_query.as_str())); |
212 | | } |
213 | | } |
214 | | |
215 | | #[test] |
216 | | fn build_from_empty_path_and_query() { |
217 | | let uri = Builder::new() |
218 | | .scheme(Scheme::HTTP) |
219 | | .authority("localhost:8080") |
220 | | .path_and_query("") |
221 | | .build() |
222 | | .unwrap(); |
223 | | |
224 | | assert_eq!(uri, "http://localhost:8080"); |
225 | | assert_eq!(uri.path(), "/"); |
226 | | } |
227 | | |
228 | | #[test] |
229 | | fn empty_path_and_query_remains_strict() { |
230 | | assert!(PathAndQuery::try_from("").is_err()); |
231 | | } |
232 | | |
233 | | #[test] |
234 | | fn authority_form_path_and_query_remains_strict() { |
235 | | assert!(Builder::new() |
236 | | .path_and_query("localhost:8080") |
237 | | .build() |
238 | | .is_err()); |
239 | | } |
240 | | |
241 | | #[test] |
242 | | fn build_from_uri() { |
243 | | let original_uri = Uri::default(); |
244 | | let uri = Builder::from(original_uri.clone()).build().unwrap(); |
245 | | assert_eq!(original_uri, uri); |
246 | | } |
247 | | |
248 | | #[test] |
249 | | fn build_star_for_http2() { |
250 | | let uri = Builder::new() |
251 | | .scheme("https") |
252 | | .authority("example.com") |
253 | | .path_and_query("*") |
254 | | .build() |
255 | | .unwrap(); |
256 | | |
257 | | assert_eq!(uri.scheme(), Some(&Scheme::HTTPS)); |
258 | | assert_eq!(uri.host(), Some("example.com")); |
259 | | assert_eq!(uri.path(), "*"); |
260 | | } |
261 | | } |