mod error;
use self::error::ParseUriErr;
use crate::*;
use shared_bytes::SharedStr;
pub use self::error::ParseUriError;
pub(crate) fn parse_uri(input: SharedStr) -> Result<Uri, ParseUriError> {
parse_uri_internal(input).map_err(ParseUriError)
}
pub(crate) fn parse_uri_ref(input: SharedStr) -> Result<UriRef, ParseUriError> {
parse_uri_ref_internal(input).map_err(ParseUriError)
}
type RemainderResult<T, E> = Result<(T, SharedStr), E>;
fn parse_uri_internal(input: SharedStr) -> Result<Uri, ParseUriErr> {
parse_uri_ref_internal(input)?
.try_into()
.map_err(|_| ParseUriErr::MissingScheme)
}
fn parse_uri_ref_internal(input: SharedStr) -> Result<UriRef, ParseUriErr> {
let (scheme, remainder) = scheme(input).map_err(ParseUriErr::Scheme)?;
let (authority, remainder) = authority(remainder)?;
let (path, remainder) = if !remainder.is_empty()
&& (remainder.starts_with('/') || !remainder.starts_with(|c| matches!(c, '?' | '#')))
{
path(remainder).map_err(ParseUriErr::Path)?
} else {
(Path::new(), remainder)
};
let (query, remainder) = query(remainder).map_err(ParseUriErr::Query)?;
let fragment = fragment(remainder).map_err(ParseUriErr::Fragment)?;
let mut resource = Resource::new();
resource.path = path;
resource.query = query;
resource.fragment = fragment;
let mut uri = UriRef::new();
uri.scheme = scheme;
uri.authority = authority;
uri.resource = resource;
Ok(uri)
}
fn scheme(input: SharedStr) -> RemainderResult<Option<Scheme>, InvalidScheme> {
for (index, byte) in input.bytes().enumerate() {
match byte {
b':' => {
let scheme = Scheme::try_from(input.slice_cloned(..index))?;
let remainder = input.slice_into(index + 1..);
return Ok((Some(scheme), remainder));
}
b'/' | b'?' | b'#' => return Ok((None, input)),
_ => continue,
}
}
Ok((None, input))
}
fn authority(input: SharedStr) -> RemainderResult<Option<Authority>, ParseUriErr> {
let remainder = match input.strip_prefix("//") {
Some(r) => {
let r = input.range_of_subset(r);
input.slice_into(r)
}
None => return Ok((None, input)),
};
let (user_info, remainder) = user_info(remainder).map_err(ParseUriErr::UserInfo)?;
let (host, remainder) = host(remainder)?;
let (port, remainder) = match remainder.strip_prefix(':') {
Some(r) => {
let r = remainder.range_of_subset(r);
let remainder = remainder.slice_into(r);
let (p, r) = port(remainder)?;
(Some(p), r)
}
None => (None, remainder),
};
let mut authority = Authority::new();
authority.user_info = user_info;
authority.host = host;
authority.port = port;
Ok((Some(authority), remainder))
}
fn user_info(input: SharedStr) -> RemainderResult<Option<UserInfo>, InvalidUserInfo> {
for (index, byte) in input.bytes().enumerate() {
match byte {
b'@' => {
let user_info = UserInfo::try_from(input.slice_cloned(..index))?;
let remainder = input.slice_into(index + 1..);
return Ok((Some(user_info), remainder));
}
b'/' | b'?' | b'#' => return Ok((None, input)),
_ => continue,
}
}
Ok((None, input))
}
fn host(input: SharedStr) -> RemainderResult<Host, ParseUriErr> {
let (host_str, remainder) = host_str(input);
match host_from_str(host_str) {
Ok(host) => Ok((host, remainder)),
Err(e) => Err(e),
}
}
fn host_str(input: SharedStr) -> (SharedStr, SharedStr) {
if input.starts_with('[') {
for (index, byte) in input.bytes().enumerate() {
match byte {
b'/' | b'?' | b'#' => {
return split_at(input, index);
}
b']' => {
let next_index = index + 1;
return split_at(input, next_index);
}
_ => continue,
}
}
} else {
for (index, byte) in input.bytes().enumerate() {
match byte {
b':' | b'/' | b'?' | b'#' => {
return split_at(input, index);
}
_ => continue,
}
}
}
(input, SharedStr::new())
}
fn host_from_str(s: SharedStr) -> Result<Host, ParseUriErr> {
if let Some(ipv6_str) = s.strip_prefix('[').and_then(|s| s.strip_suffix(']')) {
return match ipv6_str.parse() {
Ok(ipv6) => Ok(Host::Ipv6Addr(ipv6)),
Err(e) => Err(ParseUriErr::Ipv6(e)),
};
}
if let Ok(x) = s.parse() {
return Ok(Host::Ipv4Addr(x));
}
Ok(Host::Name(s.try_into().map_err(ParseUriErr::HostName)?))
}
fn port(input: SharedStr) -> RemainderResult<u16, ParseUriErr> {
let (port, remainder) = (|| {
for (index, byte) in input.bytes().enumerate() {
match byte {
b'/' | b'?' | b'#' => return Ok(split_at(input, index)),
_ if !byte.is_ascii_digit() => return Err(ParseUriErr::NonDigitPortByte(byte)),
_ => continue,
}
}
Ok((input, SharedStr::new()))
})()?;
Ok((port.parse().map_err(ParseUriErr::Port)?, remainder))
}
fn path(input: SharedStr) -> RemainderResult<Path, InvalidPath> {
let split_path_from_remainder = || {
for (index, byte) in input.bytes().enumerate() {
match byte {
b'?' | b'#' => return split_at(input, index),
_ => continue,
}
}
(input, SharedStr::new())
};
let (path, remainder) = split_path_from_remainder();
Ok((path.try_into()?, remainder))
}
fn query(input: SharedStr) -> RemainderResult<Option<Query>, InvalidQuery> {
let remainder = match input.strip_prefix('?') {
None => return Ok((None, input)),
Some(r) => {
let r = input.range_of_subset(r);
input.slice_into(r)
}
};
let split_query_from_remainder = || {
for (index, byte) in remainder.bytes().enumerate() {
match byte {
b'#' => return split_at(remainder, index),
_ => continue,
}
}
(remainder, SharedStr::new())
};
let (query, remainder) = split_query_from_remainder();
Ok((Some(query.try_into()?), remainder))
}
fn fragment(input: SharedStr) -> Result<Option<Fragment>, InvalidFragment> {
match input.strip_prefix('#') {
None => Ok(None),
Some(fragment) => {
let r = input.range_of_subset(fragment);
let fragment = input.slice_into(r).try_into()?;
Ok(Some(fragment))
}
}
}
fn split_at(s: SharedStr, index: usize) -> (SharedStr, SharedStr) {
(s.slice_cloned(..index), s.slice_into(index..))
}