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}
198