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