aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/authority.rs130
-rw-r--r--src/character_classes.rs154
-rw-r--r--src/codec.rs35
-rw-r--r--src/context.rs41
-rw-r--r--src/error.rs1
-rw-r--r--src/lib.rs22
-rw-r--r--src/parse_host_port.rs117
-rw-r--r--src/percent_encoded_character_decoder.rs17
-rw-r--r--src/uri.rs814
-rw-r--r--src/validate_ipv4_address.rs43
-rw-r--r--src/validate_ipv6_address.rs133
11 files changed, 931 insertions, 576 deletions
diff --git a/src/authority.rs b/src/authority.rs
index bde7a06..8ac8dd7 100644
--- a/src/authority.rs
+++ b/src/authority.rs
@@ -1,12 +1,17 @@
-use super::character_classes::{
- REG_NAME_NOT_PCT_ENCODED,
- USER_INFO_NOT_PCT_ENCODED,
+use super::{
+ character_classes::{
+ REG_NAME_NOT_PCT_ENCODED,
+ USER_INFO_NOT_PCT_ENCODED,
+ },
+ codec::{
+ decode_element,
+ encode_element,
+ },
+ context::Context,
+ error::Error,
+ parse_host_port::parse_host_port,
+ validate_ipv6_address::validate_ipv6_address,
};
-use super::codec::{decode_element, encode_element};
-use super::context::Context;
-use super::error::Error;
-use super::parse_host_port::parse_host_port;
-use super::validate_ipv6_address::validate_ipv6_address;
/// This is the optional part of a URI which governs the URI's namespace. It
/// typically contains a host name or IP address, and may also include a port
@@ -65,21 +70,30 @@ impl Authority {
}
/// Change the userinfo part of the Authority.
- pub fn set_userinfo<T>(&mut self, userinfo: T)
- where T: Into<Option<Vec<u8>>>
+ pub fn set_userinfo<T>(
+ &mut self,
+ userinfo: T,
+ ) where
+ T: Into<Option<Vec<u8>>>,
{
self.userinfo = userinfo.into();
}
/// Change the host name part of the Authority.
- pub fn set_host<T>(&mut self, host: T)
- where T: Into<Vec<u8>>
+ pub fn set_host<T>(
+ &mut self,
+ host: T,
+ ) where
+ T: Into<Vec<u8>>,
{
self.host = host.into();
}
/// Change the port number part of the Authority.
- pub fn set_port(&mut self, port: Option<u16>) {
+ pub fn set_port(
+ &mut self,
+ port: Option<u16>,
+ ) {
self.port = port;
}
@@ -100,50 +114,62 @@ impl Authority {
/// [`Error`](enum.Error.html) type.
#[must_use = "you parsed it; don't you want the results?"]
pub fn parse<T>(authority_string: T) -> Result<Self, Error>
- where T: AsRef<str>
+ where
+ T: AsRef<str>,
{
- let (userinfo, host_port_string) = Self::parse_userinfo(authority_string.as_ref())?;
+ let (userinfo, host_port_string) =
+ Self::parse_userinfo(authority_string.as_ref())?;
let (host, port) = parse_host_port(host_port_string)?;
- Ok(Self{
+ Ok(Self {
userinfo,
host,
port,
})
}
- fn parse_userinfo(authority: &str) -> Result<(Option<Vec<u8>>, &str), Error> {
+ fn parse_userinfo(
+ authority: &str
+ ) -> Result<(Option<Vec<u8>>, &str), Error> {
Ok(match authority.find('@') {
Some(delimiter) => (
- Some(
- decode_element(
- &authority[0..delimiter],
- &USER_INFO_NOT_PCT_ENCODED,
- Context::Userinfo
- )?
- ),
- &authority[delimiter+1..]
+ Some(decode_element(
+ &authority[0..delimiter],
+ &USER_INFO_NOT_PCT_ENCODED,
+ Context::Userinfo,
+ )?),
+ &authority[delimiter + 1..],
),
- None => (
- None,
- authority
- )
+ None => (None, authority),
})
}
}
impl std::fmt::Display for Authority {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ fn fmt(
+ &self,
+ f: &mut std::fmt::Formatter<'_>,
+ ) -> std::fmt::Result {
if let Some(userinfo) = &self.userinfo {
- write!(f, "{}@", encode_element(&userinfo, &USER_INFO_NOT_PCT_ENCODED))?;
+ write!(
+ f,
+ "{}@",
+ encode_element(&userinfo, &USER_INFO_NOT_PCT_ENCODED)
+ )?;
}
let host_to_string = String::from_utf8(self.host.clone());
match host_to_string {
- Ok(host_to_string) if validate_ipv6_address(&host_to_string).is_ok() => {
+ Ok(host_to_string)
+ if validate_ipv6_address(&host_to_string).is_ok() =>
+ {
write!(f, "[{}]", host_to_string.to_ascii_lowercase())?;
- },
- _ => {
- write!(f, "{}", encode_element(&self.host, &REG_NAME_NOT_PCT_ENCODED))?;
}
+ _ => {
+ write!(
+ f,
+ "{}",
+ encode_element(&self.host, &REG_NAME_NOT_PCT_ENCODED)
+ )?;
+ },
}
if let Some(port) = self.port {
write!(f, ":{}", port)?;
@@ -168,7 +194,8 @@ mod tests {
let test_vectors: &[TestVector] = &[
("www.example.com", None).into(),
("joe@www.example.com", Some("joe")).into(),
- ("pepe:feelsbadman@www.example.com", Some("pepe:feelsbadman")).into(),
+ ("pepe:feelsbadman@www.example.com", Some("pepe:feelsbadman"))
+ .into(),
];
for test_vector in test_vectors {
let authority = Authority::parse(test_vector.authority_string());
@@ -183,10 +210,7 @@ mod tests {
#[test]
fn userinfo_illegal_characters() {
- let test_vectors = [
- "%X@www.example.com",
- "{@www.example.com",
- ];
+ let test_vectors = ["%X@www.example.com", "{@www.example.com"];
for test_vector in &test_vectors {
let authority = Authority::parse(test_vector);
assert!(authority.is_err());
@@ -198,7 +222,7 @@ mod tests {
named_tuple!(
struct TestVector {
uri_string: &'static str,
- userinfo: &'static str
+ userinfo: &'static str,
}
);
let test_vectors: &[TestVector] = &[
@@ -223,11 +247,7 @@ mod tests {
#[test]
fn host_illegal_characters() {
- let test_vectors = [
- "%X@www.example.com",
- "@www:example.com",
- "[vX.:]",
- ];
+ let test_vectors = ["%X@www.example.com", "@www:example.com", "[vX.:]"];
for test_vector in &test_vectors {
let authority = Authority::parse(test_vector);
assert!(authority.is_err());
@@ -239,7 +259,7 @@ mod tests {
named_tuple!(
struct TestVector {
authority_string: &'static str,
- host: &'static str
+ host: &'static str,
}
);
let test_vectors: &[TestVector] = &[
@@ -257,10 +277,7 @@ mod tests {
let authority = Authority::parse(test_vector.authority_string());
assert!(authority.is_ok());
let authority = authority.unwrap();
- assert_eq!(
- test_vector.host().as_bytes(),
- authority.host()
- );
+ assert_eq!(test_vector.host().as_bytes(), authority.host());
}
}
@@ -269,10 +286,7 @@ mod tests {
let authority = Authority::parse("example.com.");
assert!(authority.is_ok());
let authority = authority.unwrap();
- assert_eq!(
- b"example.com.",
- authority.host()
- );
+ assert_eq!(b"example.com.", authority.host());
}
#[test]
@@ -289,11 +303,7 @@ mod tests {
let authority = Authority::parse(*test_vector);
assert!(authority.is_ok());
let authority = authority.unwrap();
- assert_eq!(
- normalized_host.as_bytes(),
- authority.host()
- );
+ assert_eq!(normalized_host.as_bytes(), authority.host());
}
}
-
}
diff --git a/src/character_classes.rs b/src/character_classes.rs
index a9e7b37..72ec6c3 100644
--- a/src/character_classes.rs
+++ b/src/character_classes.rs
@@ -3,127 +3,117 @@ use std::collections::HashSet;
// This is the character set containing just the alphabetic characters
// from the ASCII character set.
-pub static ALPHA: Lazy<HashSet<char>> = Lazy::new(||
- ('a'..='z')
- .chain('A'..='Z')
- .collect()
-);
+pub static ALPHA: Lazy<HashSet<char>> =
+ Lazy::new(|| ('a'..='z').chain('A'..='Z').collect());
// This is the character set containing just numbers.
-pub static DIGIT: Lazy<HashSet<char>> = Lazy::new(||
- ('0'..='9')
- .collect()
-);
+pub static DIGIT: Lazy<HashSet<char>> = Lazy::new(|| ('0'..='9').collect());
// This is the character set containing just the characters allowed
// in a hexadecimal digit.
-pub static HEXDIG: Lazy<HashSet<char>> = Lazy::new(||
- DIGIT.iter().copied()
- .chain('A'..='F')
- .chain('a'..='f')
- .collect()
-);
+pub static HEXDIG: Lazy<HashSet<char>> = Lazy::new(|| {
+ DIGIT.iter().copied().chain('A'..='F').chain('a'..='f').collect()
+});
// This is the character set corresponds to the "unreserved" syntax
// specified in RFC 3986 (https://tools.ietf.org/html/rfc3986).
-pub static UNRESERVED: Lazy<HashSet<char>> = Lazy::new(||
- ALPHA.iter()
- .chain(DIGIT.iter())
- .chain(['-', '.', '_', '~'].iter())
- .copied()
- .collect()
-);
+pub static UNRESERVED: Lazy<HashSet<char>> = Lazy::new(|| {
+ ALPHA
+ .iter()
+ .chain(DIGIT.iter())
+ .chain(['-', '.', '_', '~'].iter())
+ .copied()
+ .collect()
+});
// This is the character set corresponds to the "sub-delims" syntax
// specified in RFC 3986 (https://tools.ietf.org/html/rfc3986).
-pub static SUB_DELIMS: Lazy<HashSet<char>> = Lazy::new(||
- [
- '!', '$', '&', '\'', '(', ')',
- '*', '+', ',', ';', '='
- ]
- .iter()
- .copied()
- .collect()
-);
+pub static SUB_DELIMS: Lazy<HashSet<char>> = Lazy::new(|| {
+ ['!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=']
+ .iter()
+ .copied()
+ .collect()
+});
// This is the character set corresponds to the second part
// of the "scheme" syntax
// specified in RFC 3986 (https://tools.ietf.org/html/rfc3986).
-pub static SCHEME_NOT_FIRST: Lazy<HashSet<char>> = Lazy::new(||
- ALPHA.iter()
- .chain(DIGIT.iter())
- .chain(['+', '-', '.'].iter())
- .copied()
- .collect()
-);
+pub static SCHEME_NOT_FIRST: Lazy<HashSet<char>> = Lazy::new(|| {
+ ALPHA
+ .iter()
+ .chain(DIGIT.iter())
+ .chain(['+', '-', '.'].iter())
+ .copied()
+ .collect()
+});
// This is the character set corresponds to the "pchar" syntax
// specified in RFC 3986 (https://tools.ietf.org/html/rfc3986),
// leaving out "pct-encoded".
-pub static PCHAR_NOT_PCT_ENCODED: Lazy<HashSet<char>> = Lazy::new(||
- UNRESERVED.iter()
- .chain(SUB_DELIMS.iter())
- .chain([':', '@'].iter())
- .copied()
- .collect()
-);
+pub static PCHAR_NOT_PCT_ENCODED: Lazy<HashSet<char>> = Lazy::new(|| {
+ UNRESERVED
+ .iter()
+ .chain(SUB_DELIMS.iter())
+ .chain([':', '@'].iter())
+ .copied()
+ .collect()
+});
// This is the character set corresponds to the "query" syntax
// and the "fragment" syntax
// specified in RFC 3986 (https://tools.ietf.org/html/rfc3986),
// leaving out "pct-encoded".
-pub static QUERY_OR_FRAGMENT_NOT_PCT_ENCODED: Lazy<HashSet<char>> = Lazy::new(||
- PCHAR_NOT_PCT_ENCODED.iter()
- .chain(['/', '?'].iter())
- .copied()
- .collect()
-);
+pub static QUERY_OR_FRAGMENT_NOT_PCT_ENCODED: Lazy<HashSet<char>> =
+ Lazy::new(|| {
+ PCHAR_NOT_PCT_ENCODED.iter().chain(['/', '?'].iter()).copied().collect()
+ });
// This is the character set almost corresponds to the "query" syntax
// specified in RFC 3986 (https://tools.ietf.org/html/rfc3986),
// leaving out "pct-encoded", except that '+' is also excluded, because
// for some web services (e.g. AWS S3) a '+' is treated as
// synonymous with a space (' ') and thus gets misinterpreted.
-pub static QUERY_NOT_PCT_ENCODED_WITHOUT_PLUS: Lazy<HashSet<char>> = Lazy::new(||
- UNRESERVED.iter()
- .chain([
- '!', '$', '&', '\'', '(', ')',
- '*', ',', ';', '=',
- ':', '@',
- '/', '?'
- ].iter())
- .copied()
- .collect()
-);
+pub static QUERY_NOT_PCT_ENCODED_WITHOUT_PLUS: Lazy<HashSet<char>> =
+ Lazy::new(|| {
+ UNRESERVED
+ .iter()
+ .chain(
+ [
+ '!', '$', '&', '\'', '(', ')', '*', ',', ';', '=', ':',
+ '@', '/', '?',
+ ]
+ .iter(),
+ )
+ .copied()
+ .collect()
+ });
// This is the character set corresponds to the "userinfo" syntax
// specified in RFC 3986 (https://tools.ietf.org/html/rfc3986),
// leaving out "pct-encoded".
-pub static USER_INFO_NOT_PCT_ENCODED: Lazy<HashSet<char>> = Lazy::new(||
- UNRESERVED.iter()
- .chain(SUB_DELIMS.iter())
- .chain([':'].iter())
- .copied()
- .collect()
-);
+pub static USER_INFO_NOT_PCT_ENCODED: Lazy<HashSet<char>> = Lazy::new(|| {
+ UNRESERVED
+ .iter()
+ .chain(SUB_DELIMS.iter())
+ .chain([':'].iter())
+ .copied()
+ .collect()
+});
// This is the character set corresponds to the "reg-name" syntax
// specified in RFC 3986 (https://tools.ietf.org/html/rfc3986),
// leaving out "pct-encoded".
-pub static REG_NAME_NOT_PCT_ENCODED: Lazy<HashSet<char>> = Lazy::new(||
- UNRESERVED.iter()
- .chain(SUB_DELIMS.iter())
- .copied()
- .collect()
-);
+pub static REG_NAME_NOT_PCT_ENCODED: Lazy<HashSet<char>> =
+ Lazy::new(|| UNRESERVED.iter().chain(SUB_DELIMS.iter()).copied().collect());
// This is the character set corresponds to the last part of
// the "IPvFuture" syntax
// specified in RFC 3986 (https://tools.ietf.org/html/rfc3986).
-pub static IPV_FUTURE_LAST_PART: Lazy<HashSet<char>> = Lazy::new(||
- UNRESERVED.iter()
- .chain(SUB_DELIMS.iter())
- .chain([':'].iter())
- .copied()
- .collect()
-);
+pub static IPV_FUTURE_LAST_PART: Lazy<HashSet<char>> = Lazy::new(|| {
+ UNRESERVED
+ .iter()
+ .chain(SUB_DELIMS.iter())
+ .chain([':'].iter())
+ .copied()
+ .collect()
+});
diff --git a/src/codec.rs b/src/codec.rs
index 1002b8e..cbf0495 100644
--- a/src/codec.rs
+++ b/src/codec.rs
@@ -1,17 +1,22 @@
-use std::collections::HashSet;
-use std::convert::TryFrom;
-use std::fmt::Write;
+use std::{
+ collections::HashSet,
+ convert::TryFrom,
+ fmt::Write,
+};
-use super::context::Context;
-use super::error::Error;
-use super::percent_encoded_character_decoder::PercentEncodedCharacterDecoder;
+use super::{
+ context::Context,
+ error::Error,
+ percent_encoded_character_decoder::PercentEncodedCharacterDecoder,
+};
pub fn decode_element<T>(
element: T,
allowed_characters: &'static HashSet<char>,
- context: Context
+ context: Context,
) -> Result<Vec<u8>, Error>
- where T: AsRef<str>
+where
+ T: AsRef<str>,
{
let mut decoding_pec = false;
let mut pec_decoder = PercentEncodedCharacterDecoder::new();
@@ -20,14 +25,10 @@ pub fn decode_element<T>(
.chars()
.filter_map(|c| {
if decoding_pec {
- pec_decoder
- .next(c)
- .map_err(Into::into)
- .transpose()
- .map(|c| {
- decoding_pec = false;
- c
- })
+ pec_decoder.next(c).map_err(Into::into).transpose().map(|c| {
+ decoding_pec = false;
+ c
+ })
} else if c == '%' {
decoding_pec = true;
None
@@ -42,7 +43,7 @@ pub fn decode_element<T>(
pub fn encode_element(
element: &[u8],
- allowed_characters: &HashSet<char>
+ allowed_characters: &HashSet<char>,
) -> String {
let mut encoding = String::with_capacity(element.len());
for ci in element {
diff --git a/src/context.rs b/src/context.rs
index bb6667e..4a50446 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -40,35 +40,20 @@ pub enum Context {
}
impl std::fmt::Display for Context {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ fn fmt(
+ &self,
+ f: &mut std::fmt::Formatter<'_>,
+ ) -> std::fmt::Result {
match self {
- Context::Fragment => {
- write!(f, "fragment")
- },
- Context::Host => {
- write!(f, "host")
- },
- Context::Ipv4Address => {
- write!(f, "IPv4 address")
- },
- Context::Ipv6Address => {
- write!(f, "IPv6 address")
- },
- Context::IpvFuture => {
- write!(f, "IPvFuture")
- },
- Context::Path => {
- write!(f, "path")
- },
- Context::Query => {
- write!(f, "query")
- },
- Context::Scheme => {
- write!(f, "scheme")
- },
- Context::Userinfo => {
- write!(f, "user info")
- },
+ Context::Fragment => write!(f, "fragment"),
+ Context::Host => write!(f, "host"),
+ Context::Ipv4Address => write!(f, "IPv4 address"),
+ Context::Ipv6Address => write!(f, "IPv6 address"),
+ Context::IpvFuture => write!(f, "IPvFuture"),
+ Context::Path => write!(f, "path"),
+ Context::Query => write!(f, "query"),
+ Context::Scheme => write!(f, "scheme"),
+ Context::Userinfo => write!(f, "user info"),
}
}
}
diff --git a/src/error.rs b/src/error.rs
index 44bd8b6..dca1fc7 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -59,4 +59,3 @@ pub enum Error {
#[error("truncated host")]
TruncatedHost,
}
-
diff --git a/src/lib.rs b/src/lib.rs
index a0fa3f2..7819bad 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -28,7 +28,10 @@
//! let uri = Uri::parse("http://www.example.com/foo?bar#baz").unwrap();
//! let authority = uri.authority().unwrap();
//! assert_eq!("www.example.com".as_bytes(), authority.host());
-//! assert_eq!(Some("www.example.com"), uri.host_to_string().unwrap().as_deref());
+//! assert_eq!(
+//! Some("www.example.com"),
+//! uri.host_to_string().unwrap().as_deref()
+//! );
//! assert_eq!("/foo", uri.path_to_string().unwrap());
//! assert_eq!(Some("bar"), uri.query_to_string().unwrap().as_deref());
//! assert_eq!(Some("baz"), uri.fragment_to_string().unwrap().as_deref());
@@ -38,7 +41,10 @@
//!
//! ```rust
//! # extern crate rhymuri;
-//! use rhymuri::{Authority, Uri};
+//! use rhymuri::{
+//! Authority,
+//! Uri,
+//! };
//!
//! let mut uri = Uri::default();
//! assert!(uri.set_scheme(String::from("http")).is_ok());
@@ -68,11 +74,13 @@ mod context;
mod error;
mod parse_host_port;
mod percent_encoded_character_decoder;
+mod uri;
mod validate_ipv4_address;
mod validate_ipv6_address;
-mod uri;
-pub use crate::authority::Authority;
-pub use crate::context::Context;
-pub use crate::error::Error;
-pub use crate::uri::Uri;
+pub use crate::{
+ authority::Authority,
+ context::Context,
+ error::Error,
+ uri::Uri,
+};
diff --git a/src/parse_host_port.rs b/src/parse_host_port.rs
index abb34e1..c5cdcc6 100644
--- a/src/parse_host_port.rs
+++ b/src/parse_host_port.rs
@@ -1,14 +1,16 @@
use std::convert::TryFrom;
-use super::character_classes::{
- HEXDIG,
- IPV_FUTURE_LAST_PART,
- REG_NAME_NOT_PCT_ENCODED,
+use super::{
+ character_classes::{
+ HEXDIG,
+ IPV_FUTURE_LAST_PART,
+ REG_NAME_NOT_PCT_ENCODED,
+ },
+ context::Context,
+ error::Error,
+ percent_encoded_character_decoder::PercentEncodedCharacterDecoder,
+ validate_ipv6_address::validate_ipv6_address,
};
-use super::context::Context;
-use super::error::Error;
-use super::percent_encoded_character_decoder::PercentEncodedCharacterDecoder;
-use super::validate_ipv6_address::validate_ipv6_address;
struct Shared {
host: Vec<u8>,
@@ -28,7 +30,7 @@ enum State {
Port(Shared),
}
-impl State{
+impl State {
fn finalize(self) -> Result<(Vec<u8>, Option<u16>), Error> {
match self {
Self::PercentEncodedCharacter(_)
@@ -49,12 +51,10 @@ impl State{
None
} else {
match state.port_string.parse::<u16>() {
- Ok(port) => {
- Some(port)
- },
+ Ok(port) => Some(port),
Err(error) => {
return Err(Error::IllegalPortNumber(error));
- }
+ },
}
};
Ok((state.host, port))
@@ -63,7 +63,7 @@ impl State{
}
fn new(host_port_string: &str) -> (Self, &str) {
- let mut shared = Shared{
+ let mut shared = Shared {
host: Vec::<u8>::new(),
host_is_reg_name: false,
ipv6_address: String::new(),
@@ -74,38 +74,39 @@ impl State{
if host_port_string.starts_with("[v") {
host_port_string = &host_port_string[2..];
shared.host.push(b'v');
- (
- Self::IpvFutureNumber(shared),
- host_port_string
- )
+ (Self::IpvFutureNumber(shared), host_port_string)
} else if host_port_string.starts_with('[') {
host_port_string = &host_port_string[1..];
- (
- Self::Ipv6Address(shared),
- host_port_string
- )
+ (Self::Ipv6Address(shared), host_port_string)
} else {
shared.host_is_reg_name = true;
- (
- Self::NotIpLiteral(shared),
- host_port_string
- )
+ (Self::NotIpLiteral(shared), host_port_string)
}
}
- fn next(self, c: char) -> Result<Self, Error> {
+ fn next(
+ self,
+ c: char,
+ ) -> Result<Self, Error> {
match self {
Self::NotIpLiteral(state) => Self::next_not_ip_literal(state, c),
- Self::PercentEncodedCharacter(state) => Self::next_percent_encoded_character(state, c),
+ Self::PercentEncodedCharacter(state) => {
+ Self::next_percent_encoded_character(state, c)
+ },
Self::Ipv6Address(state) => Self::next_ipv6_address(state, c),
- Self::IpvFutureNumber(state) => Self::next_ipv_future_number(state, c),
+ Self::IpvFutureNumber(state) => {
+ Self::next_ipv_future_number(state, c)
+ },
Self::IpvFutureBody(state) => Self::next_ipv_future_body(state, c),
Self::GarbageCheck(state) => Self::next_garbage_check(state, c),
Self::Port(state) => Self::next_port(state, c),
}
}
- fn next_not_ip_literal(state: Shared, c: char) -> Result<Self, Error> {
+ fn next_not_ip_literal(
+ state: Shared,
+ c: char,
+ ) -> Result<Self, Error> {
let mut state = state;
if c == '%' {
Ok(Self::PercentEncodedCharacter(state))
@@ -119,7 +120,10 @@ impl State{
}
}
- fn next_percent_encoded_character(state: Shared, c: char) -> Result<Self, Error> {
+ fn next_percent_encoded_character(
+ state: Shared,
+ c: char,
+ ) -> Result<Self, Error> {
let mut state = state;
// We can't use `Option::map_or` (or `Option::map_or_else`, for similar
// reasons) in this case because the closure would take ownership of
@@ -134,13 +138,18 @@ impl State{
}
}
- fn next_ipv6_address(state: Shared, c: char) -> Result<Self, Error> {
+ fn next_ipv6_address(
+ state: Shared,
+ c: char,
+ ) -> Result<Self, Error> {
let mut state = state;
if c == ']' {
validate_ipv6_address(&state.ipv6_address)?;
- state.host = state.ipv6_address.chars().map(
- |c| u8::try_from(c as u32).unwrap()
- ).collect();
+ state.host = state
+ .ipv6_address
+ .chars()
+ .map(|c| u8::try_from(c as u32).unwrap())
+ .collect();
Ok(Self::GarbageCheck(state))
} else {
state.ipv6_address.push(c);
@@ -148,7 +157,10 @@ impl State{
}
}
- fn next_ipv_future_number(state: Shared, c: char) -> Result<Self, Error> {
+ fn next_ipv_future_number(
+ state: Shared,
+ c: char,
+ ) -> Result<Self, Error> {
let mut state = state;
if c == '.' {
state.host.push(b'.');
@@ -163,7 +175,10 @@ impl State{
}
}
- fn next_ipv_future_body(state: Shared, c: char) -> Result<Self, Error> {
+ fn next_ipv_future_body(
+ state: Shared,
+ c: char,
+ ) -> Result<Self, Error> {
let mut state = state;
if c == ']' {
Ok(Self::GarbageCheck(state))
@@ -175,7 +190,10 @@ impl State{
}
}
- fn next_garbage_check(state: Shared, c: char) -> Result<Self, Error> {
+ fn next_garbage_check(
+ state: Shared,
+ c: char,
+ ) -> Result<Self, Error> {
// illegal to have anything else, unless it's a colon,
// in which case it's a port delimiter
if c == ':' {
@@ -185,22 +203,26 @@ impl State{
}
}
- fn next_port(state: Shared, c: char) -> Result<Self, Error> {
+ fn next_port(
+ state: Shared,
+ c: char,
+ ) -> Result<Self, Error> {
let mut state = state;
state.port_string.push(c);
Ok(Self::Port(state))
}
}
-pub fn parse_host_port<T>(host_port_string: T) -> Result<(Vec<u8>, Option<u16>), Error>
- where T: AsRef<str>
+pub fn parse_host_port<T>(
+ host_port_string: T
+) -> Result<(Vec<u8>, Option<u16>), Error>
+where
+ T: AsRef<str>,
{
let (machine, host_port_string) = State::new(host_port_string.as_ref());
host_port_string
.chars()
- .try_fold(machine, |machine, c| {
- machine.next(c)
- })?
+ .try_fold(machine, |machine, c| machine.next(c))?
.finalize()
}
@@ -270,11 +292,7 @@ mod tests {
#[test]
fn truncated_host() {
- let test_vectors = [
- "[::ffff:1.2.3.4/",
- "[:]/",
- "[v]/",
- ];
+ let test_vectors = ["[::ffff:1.2.3.4/", "[:]/", "[v]/"];
for test_vector in &test_vectors {
assert_eq!(
Err(Error::TruncatedHost),
@@ -292,5 +310,4 @@ mod tests {
Err(Error::IllegalPortNumber(_))
));
}
-
}
diff --git a/src/percent_encoded_character_decoder.rs b/src/percent_encoded_character_decoder.rs
index fc2e9ef..70a229e 100644
--- a/src/percent_encoded_character_decoder.rs
+++ b/src/percent_encoded_character_decoder.rs
@@ -9,7 +9,7 @@ pub struct PercentEncodedCharacterDecoder {
impl PercentEncodedCharacterDecoder {
pub fn new() -> Self {
- Self{
+ Self {
decoded_character: 0,
digits_left: 2,
}
@@ -17,7 +17,7 @@ impl PercentEncodedCharacterDecoder {
pub fn next(
&mut self,
- c: char
+ c: char,
) -> Result<Option<u8>, Error> {
self.shift_in_hex_digit(c)?;
self.digits_left -= 1;
@@ -37,7 +37,7 @@ impl PercentEncodedCharacterDecoder {
fn shift_in_hex_digit(
&mut self,
- c: char
+ c: char,
) -> Result<(), Error> {
self.decoded_character <<= 4;
if let Some(ci) = c.to_digit(16) {
@@ -48,7 +48,6 @@ impl PercentEncodedCharacterDecoder {
}
Ok(())
}
-
}
#[cfg(test)]
@@ -73,10 +72,7 @@ mod tests {
];
for test_vector in test_vectors {
let mut pec = PercentEncodedCharacterDecoder::new();
- assert_eq!(
- Ok(None),
- pec.next(test_vector.sequence()[0])
- );
+ assert_eq!(Ok(None), pec.next(test_vector.sequence()[0]));
assert_eq!(
Ok(Some(*test_vector.expected_output())),
pec.next(test_vector.sequence()[1])
@@ -86,13 +82,10 @@ mod tests {
#[test]
fn bad_sequences() {
- let test_vectors = [
- 'G', 'g', '.', 'z', '-', ' ', 'V',
- ];
+ let test_vectors = ['G', 'g', '.', 'z', '-', ' ', 'V'];
for test_vector in &test_vectors {
let mut pec = PercentEncodedCharacterDecoder::new();
assert!(pec.next(*test_vector).is_err());
}
}
-
}
diff --git a/src/uri.rs b/src/uri.rs
index 87935a5..59eca88 100644
--- a/src/uri.rs
+++ b/src/uri.rs
@@ -1,15 +1,20 @@
use std::collections::HashSet;
-use super::authority::Authority;
-use super::codec::{decode_element, encode_element};
-use super::context::Context;
-use super::error::Error;
-use super::character_classes::{
- ALPHA,
- SCHEME_NOT_FIRST,
- PCHAR_NOT_PCT_ENCODED,
- QUERY_OR_FRAGMENT_NOT_PCT_ENCODED,
- QUERY_NOT_PCT_ENCODED_WITHOUT_PLUS,
+use super::{
+ authority::Authority,
+ character_classes::{
+ ALPHA,
+ PCHAR_NOT_PCT_ENCODED,
+ QUERY_NOT_PCT_ENCODED_WITHOUT_PLUS,
+ QUERY_OR_FRAGMENT_NOT_PCT_ENCODED,
+ SCHEME_NOT_FIRST,
+ },
+ codec::{
+ decode_element,
+ encode_element,
+ },
+ context::Context,
+ error::Error,
};
/// This type is used to parse and generate URI strings to and from their
@@ -55,7 +60,10 @@ use super::character_classes::{
///
/// ```rust
/// # extern crate rhymuri;
-/// use rhymuri::{Authority, Uri};
+/// use rhymuri::{
+/// Authority,
+/// Uri,
+/// };
///
/// let mut uri = Uri::default();
/// assert!(uri.set_scheme(String::from("http")).is_ok());
@@ -96,7 +104,8 @@ impl Uri {
}
fn can_navigate_path_up_one_level<T>(path: T) -> bool
- where T: AsRef<[Vec<u8>]>
+ where
+ T: AsRef<[Vec<u8>]>,
{
let path = path.as_ref();
match path.first() {
@@ -107,19 +116,18 @@ impl Uri {
// Otherwise, we can navigate up as long as there is at least one
// segment.
Some(_) => true,
- None => false
+ None => false,
}
}
fn check_scheme<T>(scheme: T) -> Result<T, Error>
- where T: AsRef<str>
+ where
+ T: AsRef<str>,
{
match scheme.as_ref() {
"" => return Err(Error::EmptyScheme),
- scheme => scheme
- .chars()
- .enumerate()
- .try_fold((), |_, (i, c)| {
+ scheme => {
+ scheme.chars().enumerate().try_fold((), |_, (i, c)| {
let valid_characters: &HashSet<char> = if i == 0 {
&ALPHA
} else {
@@ -130,7 +138,8 @@ impl Uri {
} else {
Err(Error::IllegalCharacter(Context::Scheme))
}
- })?,
+ })?
+ },
};
Ok(scheme)
}
@@ -146,12 +155,13 @@ impl Uri {
query_or_fragment: T,
context: Context,
) -> Result<Vec<u8>, Error>
- where T: AsRef<str>
+ where
+ T: AsRef<str>,
{
decode_element(
query_or_fragment,
&QUERY_OR_FRAGMENT_NOT_PCT_ENCODED,
- context
+ context,
)
}
@@ -166,13 +176,13 @@ impl Uri {
/// # Errors
///
/// Since fragments may contain non-UTF8 byte sequences, this function may
- /// return [`Error::CannotExpressAsUtf8`](enum.Error.html#variant.CannotExpressAsUtf8).
+ /// return [`Error::CannotExpressAsUtf8`](enum.Error.html#variant.
+ /// CannotExpressAsUtf8).
#[must_use = "use the fragment return value silly programmer"]
pub fn fragment_to_string(&self) -> Result<Option<String>, Error> {
self.fragment()
.map(|fragment| {
- String::from_utf8(fragment.to_vec())
- .map_err(Into::into)
+ String::from_utf8(fragment.to_vec()).map_err(Into::into)
})
.transpose()
}
@@ -180,9 +190,7 @@ impl Uri {
/// Borrow the host portion of the Authority (if any) of the URI.
#[must_use = "why u no use host return value?"]
pub fn host(&self) -> Option<&[u8]> {
- self.authority
- .as_ref()
- .map(Authority::host)
+ self.authority.as_ref().map(Authority::host)
}
/// Convert the host portion of the Authority (if any) into a string.
@@ -191,19 +199,18 @@ impl Uri {
///
/// Since host names may contain non-UTF8 byte sequences, this function may
/// return
- /// [`Error::CannotExpressAsUtf8`](enum.Error.html#variant.CannotExpressAsUtf8).
+ /// [`Error::CannotExpressAsUtf8`](enum.Error.html#variant.
+ /// CannotExpressAsUtf8).
#[must_use = "I made that host field into a string for you; don't you want it?"]
pub fn host_to_string(&self) -> Result<Option<String>, Error> {
self.host()
- .map(|host| {
- String::from_utf8(host.to_vec())
- .map_err(Into::into)
- })
+ .map(|host| String::from_utf8(host.to_vec()).map_err(Into::into))
.transpose()
}
fn is_path_absolute<T>(path: T) -> bool
- where T: AsRef<[Vec<u8>]>
+ where
+ T: AsRef<[Vec<u8>]>,
{
matches!(path.as_ref(), [segment, ..] if segment.is_empty())
}
@@ -241,7 +248,8 @@ impl Uri {
}
fn normalize_path<T>(original_path: T) -> Vec<Vec<u8>>
- where T: AsRef<[Vec<u8>]>
+ where
+ T: AsRef<[Vec<u8>]>,
{
// Rebuild the path one segment
// at a time, removing and applying special
@@ -257,8 +265,7 @@ impl Uri {
} else if segment == b".." {
// Remove last path element
// if we can navigate up a level.
- if
- !normalized_path.is_empty()
+ if !normalized_path.is_empty()
&& Self::can_navigate_path_up_one_level(&normalized_path)
{
normalized_path.pop();
@@ -286,7 +293,7 @@ impl Uri {
(true, Some(segment)) if !segment.is_empty() => {
normalized_path.push(vec![]);
},
- _ => ()
+ _ => (),
}
normalized_path
}
@@ -300,46 +307,45 @@ impl Uri {
/// let you know what's up by returning a variant of the
/// [`Error`](enum.Error.html) type.
pub fn parse<T>(uri_string: T) -> Result<Self, Error>
- where T: AsRef<str>
+ where
+ T: AsRef<str>,
{
let (scheme, rest) = Self::parse_scheme(uri_string.as_ref())?;
- let path_end = rest
- .find(&['?', '#'][..])
- .unwrap_or_else(|| rest.len());
+ let path_end = rest.find(&['?', '#'][..]).unwrap_or_else(|| rest.len());
let authority_and_path_string = &rest[0..path_end];
let query_and_or_fragment = &rest[path_end..];
- let (authority, path) = Self::split_authority_from_path_and_parse_them(authority_and_path_string)?;
- let (fragment, possible_query) = Self::parse_fragment(query_and_or_fragment)?;
+ let (authority, path) = Self::split_authority_from_path_and_parse_them(
+ authority_and_path_string,
+ )?;
+ let (fragment, possible_query) =
+ Self::parse_fragment(query_and_or_fragment)?;
let query = Self::parse_query(possible_query)?;
- Ok(Self{
+ Ok(Self {
scheme,
authority,
path,
query,
- fragment
+ fragment,
})
}
- fn parse_fragment(query_and_or_fragment: &str) -> Result<(Option<Vec<u8>>, &str), Error> {
+ fn parse_fragment(
+ query_and_or_fragment: &str
+ ) -> Result<(Option<Vec<u8>>, &str), Error> {
if let Some(fragment_delimiter) = query_and_or_fragment.find('#') {
let fragment = Self::decode_query_or_fragment(
- &query_and_or_fragment[fragment_delimiter+1..],
- Context::Fragment
+ &query_and_or_fragment[fragment_delimiter + 1..],
+ Context::Fragment,
)?;
- Ok((
- Some(fragment),
- &query_and_or_fragment[0..fragment_delimiter]
- ))
+ Ok((Some(fragment), &query_and_or_fragment[0..fragment_delimiter]))
} else {
- Ok((
- None,
- query_and_or_fragment
- ))
+ Ok((None, query_and_or_fragment))
}
}
fn parse_path<T>(path_string: T) -> Result<Vec<Vec<u8>>, Error>
- where T: AsRef<str>
+ where
+ T: AsRef<str>,
{
match path_string.as_ref() {
"/" => {
@@ -355,23 +361,24 @@ impl Uri {
Ok(vec![])
},
- path_string => {
- path_string
- .split('/')
- .map(|segment| {
- decode_element(
- &segment,
- &PCHAR_NOT_PCT_ENCODED,
- Context::Path
- )
- })
- .collect()
- }
+ path_string => path_string
+ .split('/')
+ .map(|segment| {
+ decode_element(
+ &segment,
+ &PCHAR_NOT_PCT_ENCODED,
+ Context::Path,
+ )
+ })
+ .collect(),
}
}
- fn parse_query<T>(query_and_or_fragment: T) -> Result<Option<Vec<u8>>, Error>
- where T: AsRef<str>
+ fn parse_query<T>(
+ query_and_or_fragment: T
+ ) -> Result<Option<Vec<u8>>, Error>
+ where
+ T: AsRef<str>,
{
let query_and_or_fragment = query_and_or_fragment.as_ref();
if query_and_or_fragment.is_empty() {
@@ -379,7 +386,7 @@ impl Uri {
} else {
let query = Self::decode_query_or_fragment(
&query_and_or_fragment[1..],
- Context::Query
+ Context::Query,
)?;
Ok(Some(query))
}
@@ -390,12 +397,14 @@ impl Uri {
// or path elements, because these may have the colon
// character as well, which we might misinterpret
// as the scheme delimiter.
- let authority_or_path_delimiter_start = uri_string.find('/')
- .unwrap_or_else(|| uri_string.len());
- if let Some(scheme_end) = &uri_string[0..authority_or_path_delimiter_start].find(':') {
- let scheme = Self::check_scheme(&uri_string[0..*scheme_end])?
- .to_lowercase();
- Ok((Some(scheme), &uri_string[*scheme_end+1..]))
+ let authority_or_path_delimiter_start =
+ uri_string.find('/').unwrap_or_else(|| uri_string.len());
+ if let Some(scheme_end) =
+ &uri_string[0..authority_or_path_delimiter_start].find(':')
+ {
+ let scheme =
+ Self::check_scheme(&uri_string[0..*scheme_end])?.to_lowercase();
+ Ok((Some(scheme), &uri_string[*scheme_end + 1..]))
} else {
Ok((None, uri_string))
}
@@ -437,17 +446,13 @@ impl Uri {
///
/// Since path segments may contain non-UTF8 byte sequences, this function
/// may return
- /// [`Error::CannotExpressAsUtf8`](enum.Error.html#variant.CannotExpressAsUtf8).
+ /// [`Error::CannotExpressAsUtf8`](enum.Error.html#variant.
+ /// CannotExpressAsUtf8).
#[must_use = "we went through all that trouble to put the path into a string, and you don't want it?"]
pub fn path_to_string(&self) -> Result<String, Error> {
match &*self.path {
[segment] if segment.is_empty() => Ok("/".to_string()),
- path => Ok(
- String::from_utf8(
- path
- .join(&b"/"[..])
- )?
- ),
+ path => Ok(String::from_utf8(path.join(&b"/"[..]))?),
}
}
@@ -468,14 +473,12 @@ impl Uri {
/// # Errors
///
/// Since queries may contain non-UTF8 byte sequences, this function may
- /// return [`Error::CannotExpressAsUtf8`](enum.Error.html#variant.CannotExpressAsUtf8).
+ /// return [`Error::CannotExpressAsUtf8`](enum.Error.html#variant.
+ /// CannotExpressAsUtf8).
#[must_use = "use the query return value silly programmer"]
pub fn query_to_string(&self) -> Result<Option<String>, Error> {
self.query()
- .map(|query| {
- String::from_utf8(query.to_vec())
- .map_err(Into::into)
- })
+ .map(|query| String::from_utf8(query.to_vec()).map_err(Into::into))
.transpose()
}
@@ -498,75 +501,79 @@ impl Uri {
/// # }
/// ```
#[must_use = "why go through all that effort to resolve the URI, when you're not going to use it?!"]
- pub fn resolve(&self, relative_reference: &Self) -> Self {
- let (scheme, authority, path, query) = if relative_reference.scheme.is_some() {
- (
- relative_reference.scheme.clone(),
- relative_reference.authority.clone(),
- Self::normalize_path(&relative_reference.path),
- relative_reference.query.clone()
- )
- } else {
- relative_reference.authority.as_ref().map_or_else(
- || {
- let scheme = self.scheme.clone();
- let authority = self.authority.clone();
- if relative_reference.path.is_empty() {
- let path = self.path.clone();
- let query = if relative_reference.query.is_none() {
- self.query.clone()
- } else {
- relative_reference.query.clone()
- };
- (
- scheme,
- authority,
- path,
- query
- )
- } else {
- let query = relative_reference.query.clone();
-
- // RFC describes this as:
- // "if (R.path starts-with "/") then"
- if Self::is_path_absolute(&relative_reference.path) {
- (
- scheme,
- authority,
- relative_reference.path.clone(),
- query
- )
+ pub fn resolve(
+ &self,
+ relative_reference: &Self,
+ ) -> Self {
+ let (scheme, authority, path, query) =
+ if relative_reference.scheme.is_some() {
+ (
+ relative_reference.scheme.clone(),
+ relative_reference.authority.clone(),
+ Self::normalize_path(&relative_reference.path),
+ relative_reference.query.clone(),
+ )
+ } else {
+ relative_reference.authority.as_ref().map_or_else(
+ || {
+ let scheme = self.scheme.clone();
+ let authority = self.authority.clone();
+ if relative_reference.path.is_empty() {
+ let path = self.path.clone();
+ let query = if relative_reference.query.is_none() {
+ self.query.clone()
+ } else {
+ relative_reference.query.clone()
+ };
+ (scheme, authority, path, query)
} else {
+ let query = relative_reference.query.clone();
+
// RFC describes this as:
- // "T.path = merge(Base.path, R.path);"
- let mut path = self.path.clone();
- if path.len() > 1 {
- path.pop();
+ // "if (R.path starts-with "/") then"
+ if Self::is_path_absolute(&relative_reference.path)
+ {
+ (
+ scheme,
+ authority,
+ relative_reference.path.clone(),
+ query,
+ )
+ } else {
+ // RFC describes this as:
+ // "T.path = merge(Base.path, R.path);"
+ let mut path = self.path.clone();
+ if path.len() > 1 {
+ path.pop();
+ }
+ path.extend(
+ relative_reference.path.iter().cloned(),
+ );
+ (
+ scheme,
+ authority,
+ Self::normalize_path(&path),
+ query,
+ )
}
- path.extend(relative_reference.path.iter().cloned());
- (
- scheme,
- authority,
- Self::normalize_path(&path),
- query
- )
}
- }
- },
- |authority| (
- self.scheme.clone(),
- Some(authority.clone()),
- Self::normalize_path(&relative_reference.path),
- relative_reference.query.clone()
+ },
+ |authority| {
+ (
+ self.scheme.clone(),
+ Some(authority.clone()),
+ Self::normalize_path(&relative_reference.path),
+ relative_reference.query.clone(),
+ )
+ },
)
- )
- };
- Self{
+ };
+ Self {
scheme,
authority,
path,
query,
- fragment: relative_reference.fragment.clone()
+ fragment: relative_reference.fragment.clone(),
}
}
@@ -584,15 +591,21 @@ impl Uri {
}
/// Change the authority of the URI.
- pub fn set_authority<T>(&mut self, authority: T)
- where T: Into<Option<Authority>>
+ pub fn set_authority<T>(
+ &mut self,
+ authority: T,
+ ) where
+ T: Into<Option<Authority>>,
{
self.authority = authority.into();
}
/// Change the fragment of the URI.
- pub fn set_fragment<T>(&mut self, fragment: T)
- where T: Into<Option<Vec<u8>>>
+ pub fn set_fragment<T>(
+ &mut self,
+ fragment: T,
+ ) where
+ T: Into<Option<Vec<u8>>>,
{
self.fragment = fragment.into();
}
@@ -601,8 +614,11 @@ impl Uri {
///
/// Note: See [`path`](#method.path) for special notes about what the
/// segments of the path mean.
- pub fn set_path<T>(&mut self, path: T)
- where T: Into<Vec<Vec<u8>>>
+ pub fn set_path<T>(
+ &mut self,
+ path: T,
+ ) where
+ T: Into<Vec<Vec<u8>>>,
{
self.path = path.into();
}
@@ -612,23 +628,28 @@ impl Uri {
///
/// Note: See [`path`](#method.path) for special notes about what the
/// segments of the path mean.
- pub fn set_path_from_str<T>(&mut self, path: T)
- where T: AsRef<str>
+ pub fn set_path_from_str<T>(
+ &mut self,
+ path: T,
+ ) where
+ T: AsRef<str>,
{
match path.as_ref() {
"" => self.set_path(vec![]),
path => self.set_path(
- path
- .split('/')
+ path.split('/')
.map(|segment| segment.as_bytes().to_vec())
- .collect::<Vec<Vec<u8>>>()
+ .collect::<Vec<Vec<u8>>>(),
),
}
}
/// Change the query of the URI.
- pub fn set_query<T>(&mut self, query: T)
- where T: Into<Option<Vec<u8>>>
+ pub fn set_query<T>(
+ &mut self,
+ query: T,
+ ) where
+ T: Into<Option<Vec<u8>>>,
{
self.query = query.into();
}
@@ -640,14 +661,18 @@ impl Uri {
/// The set of characters allowed in the scheme of a URI is limited.
/// [`Error::IllegalCharacter`](enum.Error.html#variant.IllegalCharacter)
/// is returned if you try to use a character that isn't allowed.
- pub fn set_scheme<T>(&mut self, scheme: T) -> Result<(), Error>
- where T: Into<Option<String>>
+ pub fn set_scheme<T>(
+ &mut self,
+ scheme: T,
+ ) -> Result<(), Error>
+ where
+ T: Into<Option<String>>,
{
self.scheme = match scheme.into() {
Some(scheme) => {
Self::check_scheme(&scheme)?;
Some(scheme)
- }
+ },
None => None,
};
Ok(())
@@ -656,7 +681,8 @@ impl Uri {
fn split_authority_from_path_and_parse_them<T>(
authority_and_path_string: T
) -> Result<(Option<Authority>, Vec<Vec<u8>>), Error>
- where T: AsRef<str>
+ where
+ T: AsRef<str>,
{
// Split authority from path. If there is an authority, parse it.
let authority_and_path_string = authority_and_path_string.as_ref();
@@ -665,7 +691,8 @@ impl Uri {
let authority_and_path_string = &authority_and_path_string[2..];
// First separate the authority from the path.
- let authority_end = authority_and_path_string.find('/')
+ let authority_end = authority_and_path_string
+ .find('/')
.unwrap_or_else(|| authority_and_path_string.len());
let authority_string = &authority_and_path_string[0..authority_end];
let path_string = &authority_and_path_string[authority_end..];
@@ -723,20 +750,23 @@ impl Uri {
/// # Errors
///
/// Since fragments may contain non-UTF8 byte sequences, this function may
- /// return [`Error::CannotExpressAsUtf8`](enum.Error.html#variant.CannotExpressAsUtf8).
+ /// return [`Error::CannotExpressAsUtf8`](enum.Error.html#variant.
+ /// CannotExpressAsUtf8).
#[must_use = "come on, you intended to use that userinfo return value, didn't you?"]
pub fn userinfo_to_string(&self) -> Result<Option<String>, Error> {
self.userinfo()
.map(|userinfo| {
- String::from_utf8(userinfo.to_vec())
- .map_err(Into::into)
+ String::from_utf8(userinfo.to_vec()).map_err(Into::into)
})
.transpose()
}
}
impl std::fmt::Display for Uri {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ fn fmt(
+ &self,
+ f: &mut std::fmt::Formatter<'_>,
+ ) -> std::fmt::Result {
if let Some(scheme) = &self.scheme {
write!(f, "{}:", scheme)?;
}
@@ -744,10 +774,7 @@ impl std::fmt::Display for Uri {
write!(f, "//{}", authority)?;
}
// Special case: absolute but otherwise empty path.
- if
- Self::is_path_absolute(&self.path)
- && self.path.len() == 1
- {
+ if Self::is_path_absolute(&self.path) && self.path.len() == 1 {
write!(f, "/")?;
}
for (i, segment) in self.path.iter().enumerate() {
@@ -757,10 +784,18 @@ impl std::fmt::Display for Uri {
}
}
if let Some(query) = &self.query {
- write!(f, "?{}", encode_element(query, &QUERY_NOT_PCT_ENCODED_WITHOUT_PLUS))?;
+ write!(
+ f,
+ "?{}",
+ encode_element(query, &QUERY_NOT_PCT_ENCODED_WITHOUT_PLUS)
+ )?;
}
if let Some(fragment) = &self.fragment {
- write!(f, "#{}", encode_element(fragment, &QUERY_OR_FRAGMENT_NOT_PCT_ENCODED))?;
+ write!(
+ f,
+ "#{}",
+ encode_element(fragment, &QUERY_OR_FRAGMENT_NOT_PCT_ENCODED)
+ )?;
}
Ok(())
}
@@ -788,7 +823,10 @@ mod tests {
let uri = uri.unwrap();
assert_eq!(Some("http"), uri.scheme());
assert_eq!(Some(&b"www.example.com"[..]), uri.host());
- assert_eq!(Some("www.example.com"), uri.host_to_string().unwrap().as_deref());
+ assert_eq!(
+ Some("www.example.com"),
+ uri.host_to_string().unwrap().as_deref()
+ );
assert_eq!(uri.path_to_string().unwrap(), "/foo/bar");
}
@@ -835,7 +873,7 @@ mod tests {
named_tuple!(
struct TestVector {
uri_string: &'static str,
- is_relative_reference: bool
+ is_relative_reference: bool,
}
);
let test_vectors: &[TestVector] = &[
@@ -860,7 +898,7 @@ mod tests {
named_tuple!(
struct TestVector {
uri_string: &'static str,
- contains_relative_path: bool
+ contains_relative_path: bool,
}
);
let test_vectors: &[TestVector] = &[
@@ -868,7 +906,6 @@ mod tests {
("http://www.example.com", false).into(),
("/", false).into(),
("foo", true).into(),
-
// This is only a valid test vector if we understand
// correctly that an empty string IS a valid
// "relative reference" URI with an empty path.
@@ -881,7 +918,8 @@ mod tests {
assert_eq!(
*test_vector.contains_relative_path(),
uri.contains_relative_path(),
- "{}", test_index
+ "{}",
+ test_index
);
}
}
@@ -893,27 +931,56 @@ mod tests {
uri_string: &'static str,
host: &'static str,
query: Option<&'static str>,
- fragment: Option<&'static str>
+ fragment: Option<&'static str>,
}
);
let test_vectors: &[TestVector] = &[
("http://www.example.com/", "www.example.com", None, None).into(),
("http://example.com?foo", "example.com", Some("foo"), None).into(),
- ("http://www.example.com#foo", "www.example.com", None, Some("foo")).into(),
- ("http://www.example.com?foo#bar", "www.example.com", Some("foo"), Some("bar")).into(),
- ("http://www.example.com?earth?day#bar", "www.example.com", Some("earth?day"), Some("bar")).into(),
- ("http://www.example.com/spam?foo#bar", "www.example.com", Some("foo"), Some("bar")).into(),
- ("http://www.example.com/?", "www.example.com", Some(""), None).into(),
+ (
+ "http://www.example.com#foo",
+ "www.example.com",
+ None,
+ Some("foo"),
+ )
+ .into(),
+ (
+ "http://www.example.com?foo#bar",
+ "www.example.com",
+ Some("foo"),
+ Some("bar"),
+ )
+ .into(),
+ (
+ "http://www.example.com?earth?day#bar",
+ "www.example.com",
+ Some("earth?day"),
+ Some("bar"),
+ )
+ .into(),
+ (
+ "http://www.example.com/spam?foo#bar",
+ "www.example.com",
+ Some("foo"),
+ Some("bar"),
+ )
+ .into(),
+ ("http://www.example.com/?", "www.example.com", Some(""), None)
+ .into(),
];
for (test_index, test_vector) in test_vectors.iter().enumerate() {
let uri = Uri::parse(test_vector.uri_string());
assert!(uri.is_ok());
let uri = uri.unwrap();
- assert_eq!(Some(*test_vector.host()), uri.host_to_string().unwrap().as_deref());
+ assert_eq!(
+ Some(*test_vector.host()),
+ uri.host_to_string().unwrap().as_deref()
+ );
assert_eq!(
*test_vector.query(),
uri.query_to_string().unwrap().as_deref(),
- "{}", test_index
+ "{}",
+ test_index
);
assert_eq!(
*test_vector.fragment(),
@@ -943,7 +1010,7 @@ mod tests {
named_tuple!(
struct TestVector {
uri_string: &'static str,
- scheme: &'static str
+ scheme: &'static str,
}
);
let test_vectors: &[TestVector] = &[
@@ -963,7 +1030,7 @@ mod tests {
}
#[test]
- fn scheme_mixed_case () {
+ fn scheme_mixed_case() {
let test_vectors = [
"http://www.example.com/",
"hTtp://www.example.com/",
@@ -1032,7 +1099,7 @@ mod tests {
named_tuple!(
struct TestVector {
uri_string: &'static str,
- path: Vec<&'static [u8]>
+ path: Vec<&'static [u8]>,
}
);
let test_vectors: &[TestVector] = &[
@@ -1040,7 +1107,13 @@ mod tests {
("bob@/foo", vec![&b"bob@"[..], &b"foo"[..]]).into(),
("hello!", vec![&b"hello!"[..]]).into(),
("urn:hello,%20w%6Frld", vec![&b"hello, world"[..]]).into(),
- ("//example.com/foo/(bar)/", vec![&b""[..], &b"foo"[..], &b"(bar)"[..], &b""[..]]).into(),
+ ("//example.com/foo/(bar)/", vec![
+ &b""[..],
+ &b"foo"[..],
+ &b"(bar)"[..],
+ &b""[..],
+ ])
+ .into(),
];
for test_vector in test_vectors {
let uri = Uri::parse(test_vector.uri_string());
@@ -1085,7 +1158,7 @@ mod tests {
named_tuple!(
struct TestVector {
uri_string: &'static str,
- query: &'static str
+ query: &'static str,
}
);
let test_vectors: &[TestVector] = &[
@@ -1103,7 +1176,8 @@ mod tests {
assert_eq!(
Some(*test_vector.query()),
uri.query_to_string().unwrap().as_deref(),
- "{}", test_index
+ "{}",
+ test_index
);
}
}
@@ -1143,7 +1217,7 @@ mod tests {
named_tuple!(
struct TestVector {
uri_string: &'static str,
- fragment: &'static str
+ fragment: &'static str,
}
);
let test_vectors: &[TestVector] = &[
@@ -1170,7 +1244,7 @@ mod tests {
named_tuple!(
struct TestVector {
uri_string: &'static str,
- path_first_segment: &'static [u8]
+ path_first_segment: &'static [u8],
}
);
let test_vectors: &[TestVector] = &[
@@ -1253,7 +1327,8 @@ mod tests {
assert_eq!(
*test_vector.normalized_path(),
uri.path_to_string().unwrap(),
- "{}", test_vector.uri_string()
+ "{}",
+ test_vector.uri_string()
);
}
}
@@ -1279,7 +1354,7 @@ mod tests {
struct TestVector {
base_string: &'static str,
relative_reference_string: &'static str,
- target_string: &'static str
+ target_string: &'static str,
}
);
let test_vectors: &[TestVector] = &[
@@ -1307,7 +1382,6 @@ mod tests {
("http://a/b/c/d;p?q", "../..", "http://a").into(),
("http://a/b/c/d;p?q", "../../", "http://a").into(),
("http://a/b/c/d;p?q", "../../g", "http://a/g").into(),
-
// Here are some examples of our own.
("http://example.com", "foo", "http://example.com/foo").into(),
("http://example.com/", "foo", "http://example.com/foo").into(),
@@ -1322,9 +1396,12 @@ mod tests {
];
for test_vector in test_vectors {
let base_uri = Uri::parse(test_vector.base_string()).unwrap();
- let relative_reference_uri = Uri::parse(test_vector.relative_reference_string()).unwrap();
- let expected_target_uri = Uri::parse(test_vector.target_string()).unwrap();
- let actual_target_uri = dbg!(base_uri.resolve(&relative_reference_uri));
+ let relative_reference_uri =
+ Uri::parse(test_vector.relative_reference_string()).unwrap();
+ let expected_target_uri =
+ Uri::parse(test_vector.target_string()).unwrap();
+ let actual_target_uri =
+ dbg!(base_uri.resolve(&relative_reference_uri));
assert_eq!(expected_target_uri, actual_target_uri);
}
}
@@ -1362,56 +1439,278 @@ mod tests {
path: &'static str,
query: Option<&'static str>,
fragment: Option<&'static str>,
- expected_uri_string: &'static str
+ expected_uri_string: &'static str,
}
);
let test_vectors: &[TestVector] = &[
// general test vectors
- // scheme userinfo host port path query fragment expected_uri_string
- (Some("http"), Some("bob"), Some("www.example.com"), Some(8080), "/abc/def", Some("foobar"), Some("ch2"), "http://bob@www.example.com:8080/abc/def?foobar#ch2").into(),
- (Some("http"), Some("bob"), Some("www.example.com"), Some(0), "", Some("foobar"), Some("ch2"), "http://bob@www.example.com:0?foobar#ch2").into(),
- (Some("http"), Some("bob"), Some("www.example.com"), Some(0), "", Some("foobar"), Some(""), "http://bob@www.example.com:0?foobar#").into(),
- (None, None, Some("example.com"), None, "", Some("bar"), None, "//example.com?bar").into(),
- (None, None, Some("example.com"), None, "", Some(""), None, "//example.com?").into(),
- (None, None, Some("example.com"), None, "", None, None, "//example.com").into(),
- (None, None, Some("example.com"), None, "/", None, None, "//example.com/").into(),
- (None, None, Some("example.com"), None, "/xyz", None, None, "//example.com/xyz").into(),
- (None, None, Some("example.com"), None, "/xyz/", None, None, "//example.com/xyz/").into(),
- (None, None, None, None, "/", None, None, "/").into(),
- (None, None, None, None, "/xyz", None, None, "/xyz").into(),
- (None, None, None, None, "/xyz/", None, None, "/xyz/").into(),
- (None, None, None, None, "", None, None, "").into(),
- (None, None, None, None, "xyz", None, None, "xyz").into(),
- (None, None, None, None, "xyz/", None, None, "xyz/").into(),
- (None, None, None, None, "", Some("bar"), None, "?bar").into(),
- (Some("http"), None, None, None, "", Some("bar"), None, "http:?bar").into(),
- (Some("http"), None, None, None, "", None, None, "http:").into(),
- (Some("http"), None, Some("::1"), None, "", None, None, "http://[::1]").into(),
- (Some("http"), None, Some("::1.2.3.4"), None, "", None, None, "http://[::1.2.3.4]").into(),
- (Some("http"), None, Some("1.2.3.4"), None, "", None, None, "http://1.2.3.4").into(),
- (None, None, None, None, "", None, None, "").into(),
- (Some("http"), Some("bob"), None, None, "", Some("foobar"), None, "http://bob@?foobar").into(),
- (None, Some("bob"), None, None, "", Some("foobar"), None, "//bob@?foobar").into(),
- (None, Some("bob"), None, None, "", None, None, "//bob@").into(),
-
+ // scheme userinfo host port
+ // path query fragment expected_uri_string
+ (
+ Some("http"),
+ Some("bob"),
+ Some("www.example.com"),
+ Some(8080),
+ "/abc/def",
+ Some("foobar"),
+ Some("ch2"),
+ "http://bob@www.example.com:8080/abc/def?foobar#ch2",
+ )
+ .into(),
+ (
+ Some("http"),
+ Some("bob"),
+ Some("www.example.com"),
+ Some(0),
+ "",
+ Some("foobar"),
+ Some("ch2"),
+ "http://bob@www.example.com:0?foobar#ch2",
+ )
+ .into(),
+ (
+ Some("http"),
+ Some("bob"),
+ Some("www.example.com"),
+ Some(0),
+ "",
+ Some("foobar"),
+ Some(""),
+ "http://bob@www.example.com:0?foobar#",
+ )
+ .into(),
+ (
+ None,
+ None,
+ Some("example.com"),
+ None,
+ "",
+ Some("bar"),
+ None,
+ "//example.com?bar",
+ )
+ .into(),
+ (
+ None,
+ None,
+ Some("example.com"),
+ None,
+ "",
+ Some(""),
+ None,
+ "//example.com?",
+ )
+ .into(),
+ (
+ None,
+ None,
+ Some("example.com"),
+ None,
+ "",
+ None,
+ None,
+ "//example.com",
+ )
+ .into(),
+ (
+ None,
+ None,
+ Some("example.com"),
+ None,
+ "/",
+ None,
+ None,
+ "//example.com/",
+ )
+ .into(),
+ (
+ None,
+ None,
+ Some("example.com"),
+ None,
+ "/xyz",
+ None,
+ None,
+ "//example.com/xyz",
+ )
+ .into(),
+ (
+ None,
+ None,
+ Some("example.com"),
+ None,
+ "/xyz/",
+ None,
+ None,
+ "//example.com/xyz/",
+ )
+ .into(),
+ (None, None, None, None, "/", None, None, "/").into(),
+ (None, None, None, None, "/xyz", None, None, "/xyz").into(),
+ (None, None, None, None, "/xyz/", None, None, "/xyz/").into(),
+ (None, None, None, None, "", None, None, "").into(),
+ (None, None, None, None, "xyz", None, None, "xyz").into(),
+ (None, None, None, None, "xyz/", None, None, "xyz/").into(),
+ (None, None, None, None, "", Some("bar"), None, "?bar").into(),
+ (
+ Some("http"),
+ None,
+ None,
+ None,
+ "",
+ Some("bar"),
+ None,
+ "http:?bar",
+ )
+ .into(),
+ (Some("http"), None, None, None, "", None, None, "http:").into(),
+ (
+ Some("http"),
+ None,
+ Some("::1"),
+ None,
+ "",
+ None,
+ None,
+ "http://[::1]",
+ )
+ .into(),
+ (
+ Some("http"),
+ None,
+ Some("::1.2.3.4"),
+ None,
+ "",
+ None,
+ None,
+ "http://[::1.2.3.4]",
+ )
+ .into(),
+ (
+ Some("http"),
+ None,
+ Some("1.2.3.4"),
+ None,
+ "",
+ None,
+ None,
+ "http://1.2.3.4",
+ )
+ .into(),
+ (None, None, None, None, "", None, None, "").into(),
+ (
+ Some("http"),
+ Some("bob"),
+ None,
+ None,
+ "",
+ Some("foobar"),
+ None,
+ "http://bob@?foobar",
+ )
+ .into(),
+ (
+ None,
+ Some("bob"),
+ None,
+ None,
+ "",
+ Some("foobar"),
+ None,
+ "//bob@?foobar",
+ )
+ .into(),
+ (None, Some("bob"), None, None, "", None, None, "//bob@").into(),
// percent-encoded character test vectors
- // scheme userinfo host port path query fragment expected_uri_string
- (Some("http"), Some("b b"), Some("www.example.com"), Some(8080), "/abc/def", Some("foobar"), Some("ch2"), "http://b%20b@www.example.com:8080/abc/def?foobar#ch2").into(),
- (Some("http"), Some("bob"), Some("www.e ample.com"), Some(8080), "/abc/def", Some("foobar"), Some("ch2"), "http://bob@www.e%20ample.com:8080/abc/def?foobar#ch2").into(),
- (Some("http"), Some("bob"), Some("www.example.com"), Some(8080), "/a c/def", Some("foobar"), Some("ch2"), "http://bob@www.example.com:8080/a%20c/def?foobar#ch2").into(),
- (Some("http"), Some("bob"), Some("www.example.com"), Some(8080), "/abc/def", Some("foo ar"), Some("ch2"), "http://bob@www.example.com:8080/abc/def?foo%20ar#ch2").into(),
- (Some("http"), Some("bob"), Some("www.example.com"), Some(8080), "/abc/def", Some("foobar"), Some("c 2"), "http://bob@www.example.com:8080/abc/def?foobar#c%202").into(),
- (Some("http"), Some("bob"), Some("ሴ.example.com"), Some(8080), "/abc/def", Some("foobar"), None, "http://bob@%E1%88%B4.example.com:8080/abc/def?foobar").into(),
-
+ // scheme userinfo host port
+ // path query fragment expected_uri_string
+ (
+ Some("http"),
+ Some("b b"),
+ Some("www.example.com"),
+ Some(8080),
+ "/abc/def",
+ Some("foobar"),
+ Some("ch2"),
+ "http://b%20b@www.example.com:8080/abc/def?foobar#ch2",
+ )
+ .into(),
+ (
+ Some("http"),
+ Some("bob"),
+ Some("www.e ample.com"),
+ Some(8080),
+ "/abc/def",
+ Some("foobar"),
+ Some("ch2"),
+ "http://bob@www.e%20ample.com:8080/abc/def?foobar#ch2",
+ )
+ .into(),
+ (
+ Some("http"),
+ Some("bob"),
+ Some("www.example.com"),
+ Some(8080),
+ "/a c/def",
+ Some("foobar"),
+ Some("ch2"),
+ "http://bob@www.example.com:8080/a%20c/def?foobar#ch2",
+ )
+ .into(),
+ (
+ Some("http"),
+ Some("bob"),
+ Some("www.example.com"),
+ Some(8080),
+ "/abc/def",
+ Some("foo ar"),
+ Some("ch2"),
+ "http://bob@www.example.com:8080/abc/def?foo%20ar#ch2",
+ )
+ .into(),
+ (
+ Some("http"),
+ Some("bob"),
+ Some("www.example.com"),
+ Some(8080),
+ "/abc/def",
+ Some("foobar"),
+ Some("c 2"),
+ "http://bob@www.example.com:8080/abc/def?foobar#c%202",
+ )
+ .into(),
+ (
+ Some("http"),
+ Some("bob"),
+ Some("ሴ.example.com"),
+ Some(8080),
+ "/abc/def",
+ Some("foobar"),
+ None,
+ "http://bob@%E1%88%B4.example.com:8080/abc/def?foobar",
+ )
+ .into(),
// normalization of IPv6 address hex digits
- // scheme userinfo host port path query fragment expected_uri_string
- (Some("http"), Some("bob"), Some("fFfF::1"), Some(8080), "/abc/def", Some("foobar"), Some("c 2"), "http://bob@[ffff::1]:8080/abc/def?foobar#c%202").into(),
+ // scheme userinfo host port path
+ // query fragment expected_uri_string
+ (
+ Some("http"),
+ Some("bob"),
+ Some("fFfF::1"),
+ Some(8080),
+ "/abc/def",
+ Some("foobar"),
+ Some("c 2"),
+ "http://bob@[ffff::1]:8080/abc/def?foobar#c%202",
+ )
+ .into(),
];
for test_vector in test_vectors {
let mut uri = Uri::default();
- assert!(uri.set_scheme(test_vector.scheme().map(ToString::to_string)).is_ok());
- if
- test_vector.userinfo().is_some()
+ assert!(uri
+ .set_scheme(test_vector.scheme().map(ToString::to_string))
+ .is_ok());
+ if test_vector.userinfo().is_some()
|| test_vector.host().is_some()
|| test_vector.port().is_some()
{
@@ -1426,10 +1725,7 @@ mod tests {
uri.set_path_from_str(test_vector.path());
uri.set_query(test_vector.query().map(Into::into));
uri.set_fragment(test_vector.fragment().map(Into::into));
- assert_eq!(
- *test_vector.expected_uri_string(),
- uri.to_string()
- );
+ assert_eq!(*test_vector.expected_uri_string(), uri.to_string());
}
}
@@ -1513,23 +1809,13 @@ mod tests {
for ci in 0_u8..31_u8 {
let mut uri = Uri::default();
uri.set_query(Some(vec![ci]));
- assert_eq!(
- uri.to_string(),
- format!("?%{:02X}", ci)
- );
+ assert_eq!(uri.to_string(), format!("?%{:02X}", ci));
}
}
#[test]
fn set_illegal_schemes() {
- let test_vectors = [
- "ab_de",
- "ab/de",
- "ab:de",
- "",
- "&",
- "foo&bar",
- ];
+ let test_vectors = ["ab_de", "ab/de", "ab:de", "", "&", "foo&bar"];
for test_vector in &test_vectors {
let mut uri = Uri::default();
assert!(uri.set_scheme(Some((*test_vector).to_string())).is_err());
@@ -1538,7 +1824,8 @@ mod tests {
#[test]
fn take_parts() {
- let mut uri = Uri::parse("https://www.example.com/foo?bar#baz").unwrap();
+ let mut uri =
+ Uri::parse("https://www.example.com/foo?bar#baz").unwrap();
assert_eq!(Some("https"), uri.take_scheme().as_deref());
assert_eq!("//www.example.com/foo?bar#baz", uri.to_string());
assert!(matches!(
@@ -1554,5 +1841,4 @@ mod tests {
assert_eq!("/foo", uri.to_string());
assert_eq!(None, uri.take_fragment().as_deref());
}
-
}
diff --git a/src/validate_ipv4_address.rs b/src/validate_ipv4_address.rs
index 98254df..3c524ce 100644
--- a/src/validate_ipv4_address.rs
+++ b/src/validate_ipv4_address.rs
@@ -1,10 +1,10 @@
#![warn(clippy::pedantic)]
-use super::character_classes::{
- DIGIT,
+use super::{
+ character_classes::DIGIT,
+ context::Context,
+ error::Error,
};
-use super::context::Context;
-use super::error::Error;
struct Shared {
num_groups: usize,
@@ -20,7 +20,9 @@ impl State {
fn finalize(self) -> Result<(), Error> {
match self {
Self::NotInOctet(_) => Err(Error::TruncatedHost),
- Self::ExpectDigitOrDot(state) => Self::finalize_expect_digit_or_dot(state),
+ Self::ExpectDigitOrDot(state) => {
+ Self::finalize_expect_digit_or_dot(state)
+ },
}
}
@@ -40,20 +42,28 @@ impl State {
}
fn new() -> Self {
- Self::NotInOctet(Shared{
+ Self::NotInOctet(Shared {
num_groups: 0,
octet_buffer: String::new(),
})
}
- fn next(self, c: char) -> Result<Self, Error> {
+ fn next(
+ self,
+ c: char,
+ ) -> Result<Self, Error> {
match self {
Self::NotInOctet(state) => Self::next_not_in_octet(state, c),
- Self::ExpectDigitOrDot(state) => Self::next_expect_digit_or_dot(state, c),
+ Self::ExpectDigitOrDot(state) => {
+ Self::next_expect_digit_or_dot(state, c)
+ },
}
}
- fn next_not_in_octet(state: Shared, c: char) -> Result<Self, Error> {
+ fn next_not_in_octet(
+ state: Shared,
+ c: char,
+ ) -> Result<Self, Error> {
let mut state = state;
if DIGIT.contains(&c) {
state.octet_buffer.push(c);
@@ -63,7 +73,10 @@ impl State {
}
}
- fn next_expect_digit_or_dot(state: Shared, c: char)-> Result<Self, Error> {
+ fn next_expect_digit_or_dot(
+ state: Shared,
+ c: char,
+ ) -> Result<Self, Error> {
let mut state = state;
if c == '.' {
state.num_groups += 1;
@@ -85,13 +98,13 @@ impl State {
}
pub fn validate_ipv4_address<T>(address: T) -> Result<(), Error>
- where T: AsRef<str>
+where
+ T: AsRef<str>,
{
- address.as_ref()
+ address
+ .as_ref()
.chars()
- .try_fold(State::new(), |machine, c| {
- machine.next(c)
- })?
+ .try_fold(State::new(), |machine, c| machine.next(c))?
.finalize()
}
diff --git a/src/validate_ipv6_address.rs b/src/validate_ipv6_address.rs
index 502da65..1f3961f 100644
--- a/src/validate_ipv6_address.rs
+++ b/src/validate_ipv6_address.rs
@@ -1,12 +1,14 @@
#![warn(clippy::pedantic)]
-use super::character_classes::{
- DIGIT,
- HEXDIG,
+use super::{
+ character_classes::{
+ DIGIT,
+ HEXDIG,
+ },
+ context::Context,
+ error::Error,
+ validate_ipv4_address::validate_ipv4_address,
};
-use super::context::Context;
-use super::error::Error;
-use super::validate_ipv4_address::validate_ipv4_address;
enum MachineExitStatus<'a> {
Error(Error),
@@ -40,20 +42,22 @@ enum State<'a> {
impl<'a> State<'a> {
fn finalize(mut self) -> Result<(), Error> {
match &mut self {
- Self::InGroupNotIpv4(state)
- | Self::InGroupCouldBeIpv4(state) => {
+ Self::InGroupNotIpv4(state) | Self::InGroupCouldBeIpv4(state) => {
// count trailing group
state.num_groups += 1;
},
Self::InGroupIpv4(state) => {
- validate_ipv4_address(&state.address[state.potential_ipv4_address_start..])?;
+ validate_ipv4_address(
+ &state.address[state.potential_ipv4_address_start..],
+ )?;
state.num_groups += 2;
},
_ => {},
};
match self {
- Self::ColonButNoGroupsYet(_)
- | Self::ColonAfterGroup(_) => Err(Error::TruncatedHost),
+ Self::ColonButNoGroupsYet(_) | Self::ColonAfterGroup(_) => {
+ Err(Error::TruncatedHost)
+ },
Self::AfterDoubleColon(state)
| Self::InGroupNotIpv4(state)
@@ -66,12 +70,12 @@ impl<'a> State<'a> {
(false, n) if n < 8 => Err(Error::TooFewAddressParts),
(_, _) => Err(Error::TooManyAddressParts),
}
- }
+ },
}
}
fn new(address: &'a str) -> Self {
- Self::NoGroupsYet(Shared{
+ Self::NoGroupsYet(Shared {
address,
num_groups: 0,
num_digits: 0,
@@ -80,19 +84,37 @@ impl<'a> State<'a> {
})
}
- fn next(self, i: usize, c: char) -> Result<Self, MachineExitStatus<'a>> {
+ fn next(
+ self,
+ i: usize,
+ c: char,
+ ) -> Result<Self, MachineExitStatus<'a>> {
match self {
Self::NoGroupsYet(state) => Self::next_no_groups_yet(state, i, c),
- Self::ColonButNoGroupsYet(state) => Self::next_colon_but_no_groups_yet(state, c),
- Self::AfterDoubleColon(state) => Self::next_after_double_colon(state, i, c),
- Self::InGroupNotIpv4(state) => Self::next_in_group_not_ipv4(state, c),
- Self::InGroupCouldBeIpv4(state) => Self::next_in_group_could_be_ipv4(state, c),
+ Self::ColonButNoGroupsYet(state) => {
+ Self::next_colon_but_no_groups_yet(state, c)
+ },
+ Self::AfterDoubleColon(state) => {
+ Self::next_after_double_colon(state, i, c)
+ },
+ Self::InGroupNotIpv4(state) => {
+ Self::next_in_group_not_ipv4(state, c)
+ },
+ Self::InGroupCouldBeIpv4(state) => {
+ Self::next_in_group_could_be_ipv4(state, c)
+ },
Self::InGroupIpv4(state) => Ok(Self::InGroupIpv4(state)),
- Self::ColonAfterGroup(state) => Self::next_colon_after_group(state, i, c),
+ Self::ColonAfterGroup(state) => {
+ Self::next_colon_after_group(state, i, c)
+ },
}
}
- fn next_no_groups_yet(state: Shared<'a>, i: usize, c: char) -> Result<Self, MachineExitStatus> {
+ fn next_no_groups_yet(
+ state: Shared<'a>,
+ i: usize,
+ c: char,
+ ) -> Result<Self, MachineExitStatus> {
let mut state = state;
if c == ':' {
Ok(Self::ColonButNoGroupsYet(state))
@@ -108,7 +130,10 @@ impl<'a> State<'a> {
}
}
- fn next_colon_but_no_groups_yet(state: Shared<'a>, c: char) -> Result<Self, MachineExitStatus> {
+ fn next_colon_but_no_groups_yet(
+ state: Shared<'a>,
+ c: char,
+ ) -> Result<Self, MachineExitStatus> {
let mut state = state;
if c == ':' {
state.double_colon_encountered = true;
@@ -118,7 +143,11 @@ impl<'a> State<'a> {
}
}
- fn next_after_double_colon(state: Shared<'a>, i: usize, c: char) -> Result<Self, MachineExitStatus> {
+ fn next_after_double_colon(
+ state: Shared<'a>,
+ i: usize,
+ c: char,
+ ) -> Result<Self, MachineExitStatus> {
let mut state = state;
state.num_digits += 1;
if state.num_digits > 4 {
@@ -133,7 +162,10 @@ impl<'a> State<'a> {
}
}
- fn next_in_group_not_ipv4(state: Shared<'a>, c: char) -> Result<Self, MachineExitStatus> {
+ fn next_in_group_not_ipv4(
+ state: Shared<'a>,
+ c: char,
+ ) -> Result<Self, MachineExitStatus> {
let mut state = state;
if c == ':' {
state.num_digits = 0;
@@ -151,7 +183,10 @@ impl<'a> State<'a> {
}
}
- fn next_in_group_could_be_ipv4(state: Shared<'a>, c: char) -> Result<Self, MachineExitStatus> {
+ fn next_in_group_could_be_ipv4(
+ state: Shared<'a>,
+ c: char,
+ ) -> Result<Self, MachineExitStatus> {
let mut state = state;
if c == ':' {
state.num_digits = 0;
@@ -173,7 +208,11 @@ impl<'a> State<'a> {
}
}
- fn next_colon_after_group(state: Shared<'a>, i: usize, c: char) -> Result<Self, MachineExitStatus> {
+ fn next_colon_after_group(
+ state: Shared<'a>,
+ i: usize,
+ c: char,
+ ) -> Result<Self, MachineExitStatus> {
let mut state = state;
if c == ':' {
if state.double_colon_encountered {
@@ -196,17 +235,18 @@ impl<'a> State<'a> {
}
pub fn validate_ipv6_address<T>(address: T) -> Result<(), Error>
- where T: AsRef<str>
+where
+ T: AsRef<str>,
{
let address = address.as_ref();
address
.char_indices()
- .try_fold(State::new(address), |machine, (i, c)| {
- machine.next(i, c)
- })
+ .try_fold(State::new(address), |machine, (i, c)| machine.next(i, c))
.or_else(|machine_exit_status| match machine_exit_status {
- MachineExitStatus::Ipv4Trailer(state) => Ok(State::InGroupIpv4(state)),
- MachineExitStatus::Error(error) => Err(error)
+ MachineExitStatus::Ipv4Trailer(state) => {
+ Ok(State::InGroupIpv4(state))
+ },
+ MachineExitStatus::Error(error) => Err(error),
})?
.finalize()
}
@@ -245,18 +285,32 @@ mod tests {
);
let test_vectors: &[TestVector] = &[
("::fFfF::1", Error::TooManyDoubleColons).into(),
- ("::ffff:1.2.x.4", Error::IllegalCharacter(Context::Ipv4Address)).into(),
+ ("::ffff:1.2.x.4", Error::IllegalCharacter(Context::Ipv4Address))
+ .into(),
("::ffff:1.2.3.4.8", Error::TooManyAddressParts).into(),
("::ffff:1.2.3", Error::TooFewAddressParts).into(),
("::ffff:1.2.3.", Error::TruncatedHost).into(),
("::ffff:1.2.3.256", Error::InvalidDecimalOctet).into(),
- ("::fxff:1.2.3.4", Error::IllegalCharacter(Context::Ipv6Address)).into(),
- ("::ffff:1.2.3.-4", Error::IllegalCharacter(Context::Ipv4Address)).into(),
- ("::ffff:1.2.3. 4", Error::IllegalCharacter(Context::Ipv4Address)).into(),
- ("::ffff:1.2.3.4 ", Error::IllegalCharacter(Context::Ipv4Address)).into(),
- ("2001:db8:85a3:8d3:1319:8a2e:370:7348:0000", Error::TooManyAddressParts).into(),
- ("2001:db8:85a3:8d3:1319:8a2e:370:7348::1", Error::TooManyAddressParts).into(),
- ("2001:db8:85a3:8d3:1319:8a2e:370::1", Error::TooManyAddressParts).into(),
+ ("::fxff:1.2.3.4", Error::IllegalCharacter(Context::Ipv6Address))
+ .into(),
+ ("::ffff:1.2.3.-4", Error::IllegalCharacter(Context::Ipv4Address))
+ .into(),
+ ("::ffff:1.2.3. 4", Error::IllegalCharacter(Context::Ipv4Address))
+ .into(),
+ ("::ffff:1.2.3.4 ", Error::IllegalCharacter(Context::Ipv4Address))
+ .into(),
+ (
+ "2001:db8:85a3:8d3:1319:8a2e:370:7348:0000",
+ Error::TooManyAddressParts,
+ )
+ .into(),
+ (
+ "2001:db8:85a3:8d3:1319:8a2e:370:7348::1",
+ Error::TooManyAddressParts,
+ )
+ .into(),
+ ("2001:db8:85a3:8d3:1319:8a2e:370::1", Error::TooManyAddressParts)
+ .into(),
("2001:db8:85a3::8a2e:0:", Error::TruncatedHost).into(),
("2001:db8:85a3::8a2e::", Error::TooManyDoubleColons).into(),
("20001:db8:85a3::1", Error::TooManyDigits).into(),
@@ -273,5 +327,4 @@ mod tests {
);
}
}
-
}