diff --git a/src/uri/authority.rs b/src/uri/authority.rs index 189bbff8..821466be 100644 --- a/src/uri/authority.rs +++ b/src/uri/authority.rs @@ -8,7 +8,7 @@ use std::str::FromStr; use bytes::Bytes; use byte_str::ByteStr; -use super::{ErrorKind, InvalidUri, InvalidUriBytes, URI_CHARS}; +use super::{ErrorKind, InvalidUri, InvalidUriBytes, URI_CHARS, Port}; /// Represents the authority component of a URI. #[derive(Clone)] @@ -180,12 +180,18 @@ impl Authority { host(self.as_str()) } - /// Get the port of this `Authority`. + #[deprecated(since="0.2.0", note="please use `port_part` instead")] + #[doc(hidden)] + pub fn port(&self) -> Option { + self.port_part().and_then(|p| Some(p.as_u16())) + } + + /// Get the port part of this `Authority`. /// /// The port subcomponent of authority is designated by an optional port - /// number in decimal following the host and delimited from it by a single - /// colon (":") character. A value is only returned if one is specified in - /// the URI string, i.e., default port values are **not** returned. + /// number following the host and delimited from it by a single colon (":") + /// character. It can be turned into a decimal port number with the `as_u16` + /// method or as a `str` with the `as_str` method. /// /// ```notrust /// abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1 @@ -202,7 +208,8 @@ impl Authority { /// # use http::uri::Authority; /// let authority: Authority = "example.org:80".parse().unwrap(); /// - /// assert_eq!(authority.port(), Some(80)); + /// let port = authority.port_part().unwrap(); + /// assert_eq!(port.as_u16(), 80); /// ``` /// /// Authority without port @@ -211,13 +218,13 @@ impl Authority { /// # use http::uri::Authority; /// let authority: Authority = "example.org".parse().unwrap(); /// - /// assert!(authority.port().is_none()); + /// assert!(authority.port_part().is_none()); /// ``` - pub fn port(&self) -> Option { - let s = self.as_str(); - s.rfind(":").and_then(|i| { - u16::from_str(&s[i+1..]).ok() - }) + pub fn port_part(&self) -> Option { + let bytes = self.as_str(); + bytes + .rfind(":") + .and_then(|i| Port::from_str(&bytes[i + 1..]).ok()) } /// Return a str representation of the authority diff --git a/src/uri/mod.rs b/src/uri/mod.rs index 8240fd07..72d15a16 100644 --- a/src/uri/mod.rs +++ b/src/uri/mod.rs @@ -40,9 +40,11 @@ use self::scheme::Scheme2; pub use self::authority::Authority; pub use self::path::PathAndQuery; pub use self::scheme::Scheme; +pub use self::port::Port; mod authority; mod path; +mod port; mod scheme; #[cfg(test)] mod tests; @@ -543,12 +545,18 @@ impl Uri { self.authority_part().map(|a| a.host()) } - /// Get the port of this `Uri`. + #[deprecated(since="0.2.0", note="please use `port_part` instead")] + #[doc(hidden)] + pub fn port(&self) -> Option { + self.port_part().and_then(|p| Some(p.as_u16())) + } + + /// Get the port part of this `Uri`. /// /// The port subcomponent of authority is designated by an optional port - /// number in decimal following the host and delimited from it by a single - /// colon (":") character. A value is only returned if one is specified in - /// the URI string, i.e., default port values are **not** returned. + /// number following the host and delimited from it by a single colon (":") + /// character. It can be turned into a decimal port number with the `as_u16` + /// method or as a `str` with the `as_str` method. /// /// ```notrust /// abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1 @@ -562,10 +570,11 @@ impl Uri { /// Absolute URI with port /// /// ``` - /// # use http::Uri; + /// # use http::{Uri, uri::Port}; /// let uri: Uri = "http://example.org:80/hello/world".parse().unwrap(); /// - /// assert_eq!(uri.port(), Some(80)); + /// let port = uri.port_part().unwrap(); + /// assert_eq!(port.as_u16(), 80); /// ``` /// /// Absolute URI without port @@ -574,7 +583,7 @@ impl Uri { /// # use http::Uri; /// let uri: Uri = "http://example.org/hello/world".parse().unwrap(); /// - /// assert!(uri.port().is_none()); + /// assert!(uri.port_part().is_none()); /// ``` /// /// Relative URI @@ -583,11 +592,11 @@ impl Uri { /// # use http::Uri; /// let uri: Uri = "/hello/world".parse().unwrap(); /// - /// assert!(uri.port().is_none()); + /// assert!(uri.port_part().is_none()); /// ``` - pub fn port(&self) -> Option { + pub fn port_part(&self) -> Option { self.authority_part() - .and_then(|a| a.port()) + .and_then(|a| a.port_part()) } /// Get the query string of this `Uri`, starting after the `?`. diff --git a/src/uri/port.rs b/src/uri/port.rs new file mode 100644 index 00000000..76501b93 --- /dev/null +++ b/src/uri/port.rs @@ -0,0 +1,64 @@ +use std::{fmt, num}; +use std::str::FromStr; + +/// The port component of a URI. +#[derive(Debug)] +pub struct Port<'a> { + bytes: &'a str, + port: u16, +} + +impl<'a> Port<'a> { + /// Converts a `str` to a port number. + /// + /// The supplied `str` must be a valid u16. + pub(crate) fn from_str(bytes: &'a str) -> Result { + u16::from_str(bytes).and_then(|port| Ok(Port { port, bytes })) + } + + /// Returns the port number as a `u16`. + /// + /// # Examples + /// + /// Port as `u16` + /// + /// ``` + /// # use http::uri::Authority; + /// let authority: Authority = "example.org:80".parse().unwrap(); + /// + /// let port = authority.port_part().unwrap(); + /// assert_eq!(port.as_u16(), 80); + /// ``` + pub fn as_u16(&self) -> u16 { + self.port + } + + /// Returns the port number as a `str`. + /// + /// # Examples + /// + /// Port as `str`. + /// + /// ``` + /// # use http::uri::Authority; + /// let authority: Authority = "example.org:80".parse().unwrap(); + /// + /// let port = authority.port_part().unwrap(); + /// assert_eq!(port.as_str(), "80"); + /// ``` + pub fn as_str(&self) -> &'a str { + self.bytes + } +} + +impl<'a> PartialEq for Port<'a> { + fn eq(&self, other: &Self) -> bool { + self.port == other.port + } +} + +impl<'a> fmt::Display for Port<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "{}", self.bytes) + } +} diff --git a/src/uri/tests.rs b/src/uri/tests.rs index 324f670d..1fe8d8b4 100644 --- a/src/uri/tests.rs +++ b/src/uri/tests.rs @@ -1,6 +1,6 @@ use std::str::FromStr; -use super::{ErrorKind, InvalidUri, Uri, URI_CHARS}; +use super::{ErrorKind, InvalidUri, Uri, URI_CHARS, Port}; #[test] fn test_char_table() { @@ -71,7 +71,7 @@ test_parse! { path = "/chunks", query = None, host = Some("127.0.0.1"), - port = Some(61761), + port_part = Port::from_str("61761").ok(), } test_parse! { @@ -83,8 +83,8 @@ test_parse! { authority_part = part!("127.0.0.1:61761"), path = "/", query = None, - port = Some(61761), host = Some("127.0.0.1"), + port_part = Port::from_str("61761").ok(), } test_parse! { @@ -108,7 +108,7 @@ test_parse! { authority_part = part!("localhost"), path = "", query = None, - port = None, + port_part = None, host = Some("localhost"), } @@ -121,7 +121,7 @@ test_parse! { authority_part = part!("S"), path = "", query = None, - port = None, + port_part = None, host = Some("S"), } @@ -135,7 +135,7 @@ test_parse! { path = "", query = None, host = Some("localhost"), - port = Some(3000), + port_part = Port::from_str("3000").ok(), } @@ -149,7 +149,7 @@ test_parse! { host = Some("127.0.0.1"), path = "/", query = None, - port = Some(80), + port_part = Port::from_str("80").ok(), } test_parse! { @@ -162,7 +162,7 @@ test_parse! { host = Some("127.0.0.1"), path = "/", query = None, - port = Some(443), + port_part = Port::from_str("443").ok(), } test_parse! { @@ -175,7 +175,7 @@ test_parse! { host = Some("127.0.0.1"), path = "/", query = None, - port = None, + port_part = None, } test_parse! { @@ -187,7 +187,7 @@ test_parse! { authority_part = part!("127.0.0.1"), path = "/path", query = Some(""), - port = None, + port_part = None, } test_parse! { @@ -199,7 +199,7 @@ test_parse! { authority_part = part!("127.0.0.1"), path = "/", query = Some("foo=bar"), - port = None, + port_part = None, } test_parse! { @@ -211,7 +211,7 @@ test_parse! { authority_part = part!("127.0.0.1"), path = "/", query = None, - port = None, + port_part = None, } test_parse! { @@ -223,7 +223,7 @@ test_parse! { authority_part = part!("127.0.0.1"), path = "/", query = None, - port = None, + port_part = None, } test_parse! { @@ -236,7 +236,7 @@ test_parse! { host = Some("127.0.0.1"), path = "/", query = None, - port = Some(1234), + port_part = Port::from_str("1234").ok(), } test_parse! { @@ -249,7 +249,7 @@ test_parse! { host = Some("127.0.0.1"), path = "/", query = None, - port = None, + port_part = None, } test_parse! { @@ -262,7 +262,7 @@ test_parse! { host = Some("127.0.0.1"), path = "/", query = None, - port = None, + port_part = None, } test_parse! { @@ -275,7 +275,7 @@ test_parse! { path = "", query = None, host = Some("localhost"), - port = Some(3000), + port_part = Port::from_str("3000").ok(), } test_parse! { @@ -288,7 +288,7 @@ test_parse! { path = "", query = None, host = Some("localhost"), - port = Some(3000), + port_part = Port::from_str("3000").ok(), } test_parse! { @@ -301,7 +301,7 @@ test_parse! { host = Some("2001:0db8:85a3:0000:0000:8a2e:0370:7334"), path = "/", query = None, - port = None, + port_part = None, } test_parse! { @@ -314,7 +314,7 @@ test_parse! { host = Some("::1"), path = "/", query = None, - port = None, + port_part = None, } test_parse! { @@ -327,7 +327,7 @@ test_parse! { host = Some("::"), path = "/", query = None, - port = None, + port_part = None, } test_parse! { @@ -340,7 +340,7 @@ test_parse! { host = Some("2001:db8::2:1"), path = "/", query = None, - port = None, + port_part = None, } test_parse! { @@ -353,7 +353,7 @@ test_parse! { host = Some("2001:0db8:85a3:0000:0000:8a2e:0370:7334"), path = "/", query = None, - port = Some(8008), + port_part = Port::from_str("8008").ok(), } test_parse! { @@ -366,7 +366,7 @@ test_parse! { host = None, path = "/echo/abcdefgh_i-j%20/abcdefg_i-j%20478", query = None, - port = None, + port_part = None, } test_parse! {