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