no_std_http/uri/
builder.rs

1use core::convert::{TryFrom, TryInto};
2
3use crate::error::{Error, Result};
4use crate::uri::{Authority, Parts, PathAndQuery, Scheme};
5use crate::Uri;
6
7/// A builder for `Uri`s.
8///
9/// This type can be used to construct an instance of `Uri`
10/// through a builder pattern.
11#[derive(Debug)]
12pub struct Builder {
13    parts: Result<Parts>,
14}
15
16impl Builder {
17    /// Creates a new default instance of `Builder` to construct a `Uri`.
18    ///
19    /// # Examples
20    ///
21    /// ```
22    /// # use http::*;
23    ///
24    /// let uri = uri::Builder::new()
25    ///     .scheme("https")
26    ///     .authority("hyper.rs")
27    ///     .path_and_query("/")
28    ///     .build()
29    ///     .unwrap();
30    /// ```
31    #[inline]
32    pub fn new() -> Builder {
33        Builder::default()
34    }
35
36    /// Set the `Scheme` for this URI.
37    ///
38    /// # Examples
39    ///
40    /// ```
41    /// # use http::*;
42    ///
43    /// let mut builder = uri::Builder::new();
44    /// builder.scheme("https");
45    /// ```
46    pub fn scheme<T>(self, scheme: T) -> Self
47    where
48        Scheme: TryFrom<T>,
49        <Scheme as TryFrom<T>>::Error: Into<Error>,
50    {
51        self.map(move |mut parts| {
52            let scheme = scheme.try_into().map_err(Into::into)?;
53            parts.scheme = Some(scheme);
54            Ok(parts)
55        })
56    }
57
58    /// Set the `Authority` for this URI.
59    ///
60    /// # Examples
61    ///
62    /// ```
63    /// # use http::*;
64    ///
65    /// let uri = uri::Builder::new()
66    ///     .authority("tokio.rs")
67    ///     .build()
68    ///     .unwrap();
69    /// ```
70    pub fn authority<T>(self, auth: T) -> Self
71    where
72        Authority: TryFrom<T>,
73        <Authority as TryFrom<T>>::Error: Into<Error>,
74    {
75        self.map(move |mut parts| {
76            let auth = auth.try_into().map_err(Into::into)?;
77            parts.authority = Some(auth);
78            Ok(parts)
79        })
80    }
81
82    /// Set the `PathAndQuery` for this URI.
83    ///
84    /// # Examples
85    ///
86    /// ```
87    /// # use http::*;
88    ///
89    /// let uri = uri::Builder::new()
90    ///     .path_and_query("/hello?foo=bar")
91    ///     .build()
92    ///     .unwrap();
93    /// ```
94    pub fn path_and_query<T>(self, p_and_q: T) -> Self
95    where
96        PathAndQuery: TryFrom<T>,
97        <PathAndQuery as TryFrom<T>>::Error: Into<Error>,
98    {
99        self.map(move |mut parts| {
100            let p_and_q = p_and_q.try_into().map_err(Into::into)?;
101            parts.path_and_query = Some(p_and_q);
102            Ok(parts)
103        })
104    }
105
106    /// Consumes this builder, and tries to construct a valid `Uri` from
107    /// the configured pieces.
108    ///
109    /// # Errors
110    ///
111    /// This function may return an error if any previously configured argument
112    /// failed to parse or get converted to the internal representation. For
113    /// example if an invalid `scheme` was specified via `scheme("!@#%/^")`
114    /// the error will be returned when this function is called rather than
115    /// when `scheme` was called.
116    ///
117    /// Additionally, the various forms of URI require certain combinations of
118    /// parts to be set to be valid. If the parts don't fit into any of the
119    /// valid forms of URI, a new error is returned.
120    ///
121    /// # Examples
122    ///
123    /// ```
124    /// # use http::*;
125    ///
126    /// let uri = Uri::builder()
127    ///     .build()
128    ///     .unwrap();
129    /// ```
130    pub fn build(self) -> Result<Uri> {
131        let parts = self.parts?;
132        Uri::from_parts(parts).map_err(Into::into)
133    }
134
135    // private
136
137    fn map<F>(self, func: F) -> Self
138    where
139        F: FnOnce(Parts) -> Result<Parts>,
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
156impl From<Uri> for Builder {
157    fn from(uri: Uri) -> Self {
158        Self {
159            parts: Ok(uri.into_parts()),
160        }
161    }
162}
163
164#[cfg(test)]
165mod tests {
166    use alloc::format;
167
168    use super::*;
169
170    #[test]
171    fn build_from_str() {
172        let uri = Builder::new()
173            .scheme(Scheme::HTTP)
174            .authority("hyper.rs")
175            .path_and_query("/foo?a=1")
176            .build()
177            .unwrap();
178        assert_eq!(uri.scheme_str(), Some("http"));
179        assert_eq!(uri.authority().unwrap().host(), "hyper.rs");
180        assert_eq!(uri.path(), "/foo");
181        assert_eq!(uri.query(), Some("a=1"));
182    }
183
184    #[test]
185    fn build_from_string() {
186        for i in 1..10 {
187            let uri = Builder::new()
188                .path_and_query(format!("/foo?a={}", i))
189                .build()
190                .unwrap();
191            let expected_query = format!("a={}", i);
192            assert_eq!(uri.path(), "/foo");
193            assert_eq!(uri.query(), Some(expected_query.as_str()));
194        }
195    }
196
197    #[test]
198    fn build_from_string_ref() {
199        for i in 1..10 {
200            let p_a_q = format!("/foo?a={}", i);
201            let uri = Builder::new().path_and_query(&p_a_q).build().unwrap();
202            let expected_query = format!("a={}", i);
203            assert_eq!(uri.path(), "/foo");
204            assert_eq!(uri.query(), Some(expected_query.as_str()));
205        }
206    }
207
208    #[test]
209    fn build_from_uri() {
210        let original_uri = Uri::default();
211        let uri = Builder::from(original_uri.clone()).build().unwrap();
212        assert_eq!(original_uri, uri);
213    }
214}