From 63487201a3a9ad36d300a0d928b9459eb889a117 Mon Sep 17 00:00:00 2001 From: Prasanna Loganathar Date: Wed, 3 Apr 2019 00:36:59 +0530 Subject: [PATCH] add TryFrom impls for path, scheme, uri and authority --- src/uri/authority.rs | 49 ++++++++++--- src/uri/mod.rs | 129 +++++++++++++++++++------------- src/uri/path.rs | 171 +++++++++++++++++++++++++------------------ src/uri/scheme.rs | 52 +++++++++---- 4 files changed, 254 insertions(+), 147 deletions(-) diff --git a/src/uri/authority.rs b/src/uri/authority.rs index 724712cc..de551718 100644 --- a/src/uri/authority.rs +++ b/src/uri/authority.rs @@ -4,6 +4,7 @@ use std::ascii::AsciiExt; use std::{cmp, fmt, str}; use std::hash::{Hash, Hasher}; use std::str::FromStr; +use std::convert::TryFrom; use bytes::Bytes; @@ -24,8 +25,7 @@ impl Authority { /// Attempt to convert an `Authority` from `Bytes`. /// - /// This function will be replaced by a `TryFrom` implementation once the - /// trait lands in stable. + /// This function has been replaced by `TryFrom` implementation. /// /// # Examples /// @@ -44,15 +44,7 @@ impl Authority { /// # } /// ``` pub fn from_shared(s: Bytes) -> Result { - let authority_end = Authority::parse_non_empty(&s[..]).map_err(InvalidUriBytes)?; - - if authority_end != s.len() { - return Err(ErrorKind::InvalidUriChar.into()); - } - - Ok(Authority { - data: unsafe { ByteStr::from_utf8_unchecked(s) }, - }) + TryFrom::try_from(s) } /// Attempt to convert an `Authority` from a static string. @@ -272,6 +264,41 @@ impl Authority { } } +impl TryFrom for Authority { + type Error = InvalidUriBytes; + /// Attempt to convert an `Authority` from `Bytes`. + /// + /// This function has been replaced by `TryFrom` implementation. + /// + /// # Examples + /// + /// ``` + /// # extern crate http; + /// # use http::uri::*; + /// extern crate bytes; + /// + /// use bytes::Bytes; + /// + /// # pub fn main() { + /// let bytes = Bytes::from("example.com"); + /// let authority = Authority::from_shared(bytes).unwrap(); + /// + /// assert_eq!(authority.host(), "example.com"); + /// # } + /// ``` + fn try_from(s: Bytes) -> Result { + let authority_end = Authority::parse_non_empty(&s[..]).map_err(InvalidUriBytes)?; + + if authority_end != s.len() { + return Err(ErrorKind::InvalidUriChar.into()); + } + + Ok(Authority { + data: unsafe { ByteStr::from_utf8_unchecked(s) }, + }) + } +} + impl AsRef for Authority { fn as_ref(&self) -> &str { self.as_str() diff --git a/src/uri/mod.rs b/src/uri/mod.rs index 2c3be20b..5628f307 100644 --- a/src/uri/mod.rs +++ b/src/uri/mod.rs @@ -34,6 +34,7 @@ use std::ascii::AsciiExt; use std::hash::{Hash, Hasher}; use std::str::{self, FromStr}; use std::error::Error; +use std::convert::TryFrom; use self::scheme::Scheme2; @@ -242,8 +243,7 @@ impl Uri { /// Attempt to convert a `Uri` from `Bytes` /// - /// This function will be replaced by a `TryFrom` implementation once the - /// trait lands in stable. + /// This function has been replaced by `TryFrom` implementation. /// /// # Examples /// @@ -263,55 +263,7 @@ impl Uri { /// # } /// ``` pub fn from_shared(s: Bytes) -> Result { - use self::ErrorKind::*; - - if s.len() > MAX_LEN { - return Err(TooLong.into()); - } - - match s.len() { - 0 => { - return Err(Empty.into()); - } - 1 => { - match s[0] { - b'/' => { - return Ok(Uri { - scheme: Scheme::empty(), - authority: Authority::empty(), - path_and_query: PathAndQuery::slash(), - }); - } - b'*' => { - return Ok(Uri { - scheme: Scheme::empty(), - authority: Authority::empty(), - path_and_query: PathAndQuery::star(), - }); - } - _ => { - let authority = Authority::from_shared(s)?; - - return Ok(Uri { - scheme: Scheme::empty(), - authority: authority, - path_and_query: PathAndQuery::empty(), - }); - } - } - } - _ => {} - } - - if s[0] == b'/' && s[1] != b'/' { - return Ok(Uri { - scheme: Scheme::empty(), - authority: Authority::empty(), - path_and_query: PathAndQuery::from_shared(s)?, - }); - } - - parse_full(s) + TryFrom::try_from(s) } /// Convert a `Uri` from a static string. @@ -707,6 +659,81 @@ impl Uri { } } +impl TryFrom for Uri { + type Error = InvalidUriBytes; + + /// Attempt to convert a `Uri` from `Bytes` + /// + /// # Examples + /// + /// ``` + /// # extern crate http; + /// # use http::uri::*; + /// extern crate bytes; + /// + /// use bytes::Bytes; + /// + /// # pub fn main() { + /// let bytes = Bytes::from("http://example.com/foo"); + /// let uri = Uri::from_shared(bytes).unwrap(); + /// + /// assert_eq!(uri.host().unwrap(), "example.com"); + /// assert_eq!(uri.path(), "/foo"); + /// # } + /// ``` + fn try_from(s: Bytes) -> Result { + use self::ErrorKind::*; + + if s.len() > MAX_LEN { + return Err(TooLong.into()); + } + + match s.len() { + 0 => { + return Err(Empty.into()); + } + 1 => { + match s[0] { + b'/' => { + return Ok(Uri { + scheme: Scheme::empty(), + authority: Authority::empty(), + path_and_query: PathAndQuery::slash(), + }); + } + b'*' => { + return Ok(Uri { + scheme: Scheme::empty(), + authority: Authority::empty(), + path_and_query: PathAndQuery::star(), + }); + } + _ => { + let authority = Authority::from_shared(s)?; + + return Ok(Uri { + scheme: Scheme::empty(), + authority: authority, + path_and_query: PathAndQuery::empty(), + }); + } + } + } + _ => {} + } + + if s[0] == b'/' && s[1] != b'/' { + return Ok(Uri { + scheme: Scheme::empty(), + authority: Authority::empty(), + path_and_query: PathAndQuery::from_shared(s)?, + }); + } + + parse_full(s) + } +} + impl<'a> HttpTryFrom<&'a str> for Uri { type Error = InvalidUri; diff --git a/src/uri/path.rs b/src/uri/path.rs index 803dd714..c7593a70 100644 --- a/src/uri/path.rs +++ b/src/uri/path.rs @@ -1,5 +1,6 @@ use std::{cmp, fmt, str}; use std::str::FromStr; +use std::convert::TryFrom; use bytes::Bytes; @@ -19,8 +20,7 @@ const NONE: u16 = ::std::u16::MAX; impl PathAndQuery { /// Attempt to convert a `PathAndQuery` from `Bytes`. /// - /// This function will be replaced by a `TryFrom` implementation once the - /// trait lands in stable. + /// This function has been replaced by a `TryFrom` implementation. /// /// # Examples /// @@ -39,76 +39,8 @@ impl PathAndQuery { /// assert_eq!(path_and_query.query(), Some("world")); /// # } /// ``` - pub fn from_shared(mut src: Bytes) -> Result { - let mut query = NONE; - let mut fragment = None; - - // block for iterator borrow - { - let mut iter = src.as_ref().iter().enumerate(); - - // path ... - for (i, &b) in &mut iter { - // See https://url.spec.whatwg.org/#path-state - match b { - b'?' => { - debug_assert_eq!(query, NONE); - query = i as u16; - break; - } - b'#' => { - fragment = Some(i); - break; - }, - - // This is the range of bytes that don't need to be - // percent-encoded in the path. If it should have been - // percent-encoded, then error. - 0x21 | - 0x24...0x3B | - 0x3D | - 0x40...0x5F | - 0x61...0x7A | - 0x7C | - 0x7E => {}, - - _ => return Err(ErrorKind::InvalidUriChar.into()), - } - } - - // query ... - if query != NONE { - for (i, &b) in iter { - match b { - // While queries *should* be percent-encoded, most - // bytes are actually allowed... - // See https://url.spec.whatwg.org/#query-state - // - // Allowed: 0x21 / 0x24 - 0x3B / 0x3D / 0x3F - 0x7E - 0x21 | - 0x24...0x3B | - 0x3D | - 0x3F...0x7E => {}, - - b'#' => { - fragment = Some(i); - break; - }, - - _ => return Err(ErrorKind::InvalidUriChar.into()), - } - } - } - } - - if let Some(i) = fragment { - src.truncate(i); - } - - Ok(PathAndQuery { - data: unsafe { ByteStr::from_utf8_unchecked(src) }, - query: query, - }) + pub fn from_shared(src: Bytes) -> Result { + TryFrom::try_from(src) } /// Convert a `PathAndQuery` from a static string. @@ -276,6 +208,101 @@ impl PathAndQuery { } } +impl TryFrom for PathAndQuery { + type Error = InvalidUriBytes; + + /// Attempt to convert a `PathAndQuery` from `Bytes`. + /// + /// # Examples + /// + /// ``` + /// # extern crate http; + /// # use http::uri::*; + /// extern crate bytes; + /// + /// use bytes::Bytes; + /// + /// # pub fn main() { + /// let bytes = Bytes::from("/hello?world"); + /// let path_and_query = PathAndQuery::from_shared(bytes).unwrap(); + /// + /// assert_eq!(path_and_query.path(), "/hello"); + /// assert_eq!(path_and_query.query(), Some("world")); + /// # } + /// ``` + fn try_from(mut src: Bytes) -> Result { + let mut query = NONE; + let mut fragment = None; + + // block for iterator borrow + { + let mut iter = src.as_ref().iter().enumerate(); + + // path ... + for (i, &b) in &mut iter { + // See https://url.spec.whatwg.org/#path-state + match b { + b'?' => { + debug_assert_eq!(query, NONE); + query = i as u16; + break; + } + b'#' => { + fragment = Some(i); + break; + }, + + // This is the range of bytes that don't need to be + // percent-encoded in the path. If it should have been + // percent-encoded, then error. + 0x21 | + 0x24...0x3B | + 0x3D | + 0x40...0x5F | + 0x61...0x7A | + 0x7C | + 0x7E => {}, + + _ => return Err(ErrorKind::InvalidUriChar.into()), + } + } + + // query ... + if query != NONE { + for (i, &b) in iter { + match b { + // While queries *should* be percent-encoded, most + // bytes are actually allowed... + // See https://url.spec.whatwg.org/#query-state + // + // Allowed: 0x21 / 0x24 - 0x3B / 0x3D / 0x3F - 0x7E + 0x21 | + 0x24...0x3B | + 0x3D | + 0x3F...0x7E => {}, + + b'#' => { + fragment = Some(i); + break; + }, + + _ => return Err(ErrorKind::InvalidUriChar.into()), + } + } + } + } + + if let Some(i) = fragment { + src.truncate(i); + } + + Ok(PathAndQuery { + data: unsafe { ByteStr::from_utf8_unchecked(src) }, + query: query, + }) + } +} + impl HttpTryFrom for PathAndQuery { type Error = InvalidUriBytes; #[inline] diff --git a/src/uri/scheme.rs b/src/uri/scheme.rs index 3b661846..fb2210f3 100644 --- a/src/uri/scheme.rs +++ b/src/uri/scheme.rs @@ -4,6 +4,7 @@ use std::ascii::AsciiExt; use std::fmt; use std::hash::{Hash, Hasher}; use std::str::FromStr; +use std::convert::TryFrom; use bytes::Bytes; @@ -44,8 +45,7 @@ impl Scheme { /// Attempt to convert a `Scheme` from `Bytes` /// - /// This function will be replaced by a `TryFrom` implementation once the - /// trait lands in stable. + /// This function has been replaced by a `TryFrom` /// /// # Examples /// @@ -64,17 +64,7 @@ impl Scheme { /// # } /// ``` pub fn from_shared(s: Bytes) -> Result { - use self::Scheme2::*; - - match Scheme2::parse_exact(&s[..]).map_err(InvalidUriBytes)? { - None => Err(ErrorKind::InvalidScheme.into()), - Relative => Ok(Relative.into()), - Standard(p) => Ok(Standard(p).into()), - Other(_) => { - let b = unsafe { ByteStr::from_utf8_unchecked(s) }; - Ok(Other(Box::new(b)).into()) - } - } + TryFrom::try_from(s) } pub(super) fn empty() -> Self { @@ -113,6 +103,42 @@ impl Scheme { } } +impl TryFrom for Scheme { + type Error = InvalidUriBytes; + + /// Attempt to convert a `Scheme` from `Bytes` + /// + /// # Examples + /// + /// ``` + /// # extern crate http; + /// # use http::uri::*; + /// extern crate bytes; + /// + /// use bytes::Bytes; + /// + /// # pub fn main() { + /// let bytes = Bytes::from("http"); + /// let scheme = Scheme::from_shared(bytes).unwrap(); + /// + /// assert_eq!(scheme.as_str(), "http"); + /// # } + /// ``` + fn try_from(s: Bytes) -> Result { + use self::Scheme2::*; + + match Scheme2::parse_exact(&s[..]).map_err(InvalidUriBytes)? { + None => Err(ErrorKind::InvalidScheme.into()), + Relative => Ok(Relative.into()), + Standard(p) => Ok(Standard(p).into()), + Other(_) => { + let b = unsafe { ByteStr::from_utf8_unchecked(s) }; + Ok(Other(Box::new(b)).into()) + } + } + } +} + impl HttpTryFrom for Scheme { type Error = InvalidUriBytes; #[inline]