Skip to main content

http/uri/
builder.rs

1use std::convert::TryInto;
2
3use super::{Authority, Parts, PathAndQuery, Scheme};
4use 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)]
11pub struct Builder {
12    parts: Result<Parts, crate::Error>,
13}
14
15impl 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        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    pub fn build(self) -> Result<Uri, crate::Error> {
140        let parts = self.parts?;
141        Uri::from_parts(parts).map_err(Into::into)
142    }
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
156impl Default for Builder {
157    #[inline]
158    fn default() -> Builder {
159        Builder {
160            parts: Ok(Parts::default()),
161        }
162    }
163}
164
165impl From<Uri> for Builder {
166    fn from(uri: Uri) -> Self {
167        Self {
168            parts: Ok(uri.into_parts()),
169        }
170    }
171}
172
173#[cfg(test)]
174mod 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}