aboutsummaryrefslogtreecommitdiff
path: root/src/uri.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/uri.rs')
-rw-r--r--src/uri.rs814
1 files changed, 550 insertions, 264 deletions
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());
}
-
}