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 = 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        Builder {
141            parts: self.parts.and_then(func),
142        }
143    }
144}
145
146impl Default for Builder {
147    #[inline]
148    fn default() -> Builder {
149        Builder {
150            parts: Ok(Parts::default()),
151        }
152    }
153}
154
155impl From<Uri> for Builder {
156    fn from(uri: Uri) -> Self {
157        Self {
158            parts: Ok(uri.into_parts()),
159        }
160    }
161}
162
163#[cfg(test)]
164mod tests {
165    use super::*;
166
167    #[test]
168    fn build_from_str() {
169        let uri = Builder::new()
170            .scheme(Scheme::HTTP)
171            .authority("hyper.rs")
172            .path_and_query("/foo?a=1")
173            .build()
174            .unwrap();
175        assert_eq!(uri.scheme_str(), Some("http"));
176        assert_eq!(uri.authority().unwrap().host(), "hyper.rs");
177        assert_eq!(uri.path(), "/foo");
178        assert_eq!(uri.query(), Some("a=1"));
179    }
180
181    #[test]
182    fn build_from_string() {
183        for i in 1..10 {
184            let uri = Builder::new()
185                .path_and_query(format!("/foo?a={}", i))
186                .build()
187                .unwrap();
188            let expected_query = format!("a={}", i);
189            assert_eq!(uri.path(), "/foo");
190            assert_eq!(uri.query(), Some(expected_query.as_str()));
191        }
192    }
193
194    #[test]
195    fn build_from_string_ref() {
196        for i in 1..10 {
197            let p_a_q = format!("/foo?a={}", i);
198            let uri = Builder::new().path_and_query(&p_a_q).build().unwrap();
199            let expected_query = format!("a={}", i);
200            assert_eq!(uri.path(), "/foo");
201            assert_eq!(uri.query(), Some(expected_query.as_str()));
202        }
203    }
204
205    #[test]
206    fn build_from_uri() {
207        let original_uri = Uri::default();
208        let uri = Builder::from(original_uri.clone()).build().unwrap();
209        assert_eq!(original_uri, uri);
210    }
211}