http/uri/
builder.rs

1use std::convert::{TryFrom, 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        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
147impl Default for Builder {
148    #[inline]
149    fn default() -> Builder {
150        Builder {
151            parts: Ok(Parts::default()),
152        }
153    }
154}
155
156#[cfg(test)]
157mod 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}