1 | use std::convert::{TryFrom, 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 | pub fn new() -> Builder { |
32 | Builder::default() |
33 | } |
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 | Scheme: TryFrom<T>, |
48 | <Scheme as TryFrom<T>>::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 | Authority: TryFrom<T>, |
72 | <Authority as TryFrom<T>>::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 | PathAndQuery: TryFrom<T>, |
96 | <PathAndQuery as TryFrom<T>>::Error: Into<crate::Error>, |
97 | { |
98 | self.map(move |mut parts| { |
99 | let p_and_q = p_and_q.try_into().map_err(Into::into)?; |
100 | parts.path_and_query = Some(p_and_q); |
101 | Ok(parts) |
102 | }) |
103 | } |
104 | |
105 | /// Consumes this builder, and tries to construct a valid `Uri` from |
106 | /// the configured pieces. |
107 | /// |
108 | /// # Errors |
109 | /// |
110 | /// This function may return an error if any previously configured argument |
111 | /// failed to parse or get converted to the internal representation. For |
112 | /// example if an invalid `scheme` was specified via `scheme("!@#%/^")` |
113 | /// the error will be returned when this function is called rather than |
114 | /// when `scheme` was called. |
115 | /// |
116 | /// Additionally, the various forms of URI require certain combinations of |
117 | /// parts to be set to be valid. If the parts don't fit into any of the |
118 | /// valid forms of URI, a new error is returned. |
119 | /// |
120 | /// # Examples |
121 | /// |
122 | /// ``` |
123 | /// # use http::*; |
124 | /// |
125 | /// let uri = Uri::builder() |
126 | /// .build() |
127 | /// .unwrap(); |
128 | /// ``` |
129 | pub fn build(self) -> Result<Uri, crate::Error> { |
130 | let parts = self.parts?; |
131 | Uri::from_parts(parts).map_err(Into::into) |
132 | } |
133 | |
134 | // private |
135 | |
136 | fn map<F>(self, func: F) -> Self |
137 | where |
138 | F: FnOnce(Parts) -> Result<Parts, crate::Error>, |
139 | { |
140 | |
141 | Builder { |
142 | parts: self.parts.and_then(func), |
143 | } |
144 | } |
145 | } |
146 | |
147 | impl Default for Builder { |
148 | #[inline ] |
149 | fn default() -> Builder { |
150 | Builder { |
151 | parts: Ok(Parts::default()), |
152 | } |
153 | } |
154 | } |
155 | |
156 | #[cfg (test)] |
157 | mod tests { |
158 | use super::*; |
159 | |
160 | #[test] |
161 | fn build_from_str() { |
162 | let uri = Builder::new() |
163 | .scheme(Scheme::HTTP) |
164 | .authority("hyper.rs" ) |
165 | .path_and_query("/foo?a=1" ) |
166 | .build() |
167 | .unwrap(); |
168 | assert_eq!(uri.scheme_str(), Some("http" )); |
169 | assert_eq!(uri.authority().unwrap().host(), "hyper.rs" ); |
170 | assert_eq!(uri.path(), "/foo" ); |
171 | assert_eq!(uri.query(), Some("a=1" )); |
172 | } |
173 | |
174 | #[test] |
175 | fn build_from_string() { |
176 | for i in 1..10 { |
177 | let uri = Builder::new() |
178 | .path_and_query(format!("/foo?a={}" , i)) |
179 | .build() |
180 | .unwrap(); |
181 | let expected_query = format!("a={}" , i); |
182 | assert_eq!(uri.path(), "/foo" ); |
183 | assert_eq!(uri.query(), Some(expected_query.as_str())); |
184 | } |
185 | } |
186 | |
187 | #[test] |
188 | fn build_from_string_ref() { |
189 | for i in 1..10 { |
190 | let p_a_q = format!("/foo?a={}" , i); |
191 | let uri = Builder::new().path_and_query(&p_a_q).build().unwrap(); |
192 | let expected_query = format!("a={}" , i); |
193 | assert_eq!(uri.path(), "/foo" ); |
194 | assert_eq!(uri.query(), Some(expected_query.as_str())); |
195 | } |
196 | } |
197 | } |
198 | |