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]
new() -> Builder31     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     /// ```
scheme<T>(self, scheme: T) -> Self where Scheme: TryFrom<T>, <Scheme as TryFrom<T>>::Error: Into<crate::Error>,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     /// ```
authority<T>(self, auth: T) -> Self where Authority: TryFrom<T>, <Authority as TryFrom<T>>::Error: Into<crate::Error>,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     /// ```
path_and_query<T>(self, p_and_q: T) -> Self where PathAndQuery: TryFrom<T>, <PathAndQuery as TryFrom<T>>::Error: Into<crate::Error>,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     /// ```
build(self) -> Result<Uri, crate::Error>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 
map<F>(self, func: F) -> Self where F: FnOnce(Parts) -> Result<Parts, crate::Error>,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]
default() -> Builder149     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]
build_from_str()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]
build_from_string()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]
build_from_string_ref()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