use pear::parsers::*;
use pear::{parser, switch};
use uri::{Uri, Origin, Authority, Absolute, Host};
use parse::uri::tables::{is_reg_name_char, is_pchar, is_pchar_or_rchar};
use parse::uri::RawInput;
use parse::IndexedBytes;
type Result<'a, T> = ::pear::Result<T, RawInput<'a>>;
#[parser]
crate fn uri<'a>(input: &mut RawInput<'a>) -> Result<'a, Uri<'a>> {
match input.len() {
0 => return Err(pear_error!("empty URI")),
1 => switch! {
eat(b'*') => Uri::Asterisk,
eat(b'/') => Uri::Origin(Origin::new::<_, &str>("/", None)),
_ => unsafe {
let host = Host::Raw(take_n_if(1, is_reg_name_char)?);
Uri::Authority(Authority::raw(input.cow_source(), None, host, None))
}
},
_ => switch! {
peek(b'/') => Uri::Origin(origin()?),
_ => absolute_or_authority()?
}
}
}
#[parser]
crate fn origin<'a>(input: &mut RawInput<'a>) -> Result<'a, Origin<'a>> {
(peek(b'/')?, path_and_query(is_pchar)?).1
}
#[parser]
crate fn rocket_route_origin<'a>(input: &mut RawInput<'a>) -> Result<'a, Origin<'a>> {
(peek(b'/')?, path_and_query(is_pchar_or_rchar)?).1
}
#[parser]
fn path_and_query<'a, F>(input: &mut RawInput<'a>, is_good_char: F) -> Result<'a, Origin<'a>>
where F: Fn(u8) -> bool + Copy
{
let path = take_while(is_good_char)?;
let query = switch! {
eat(b'?') => Some(take_while(|c| is_good_char(c) || c == b'?')?),
_ => None
};
if path.is_empty() && query.is_none() {
Err(pear_error!("expected path or query, found neither"))
} else {
Ok(unsafe { Origin::raw(input.cow_source(), path, query) })
}
}
#[parser]
fn port_from<'a>(input: &mut RawInput<'a>, bytes: &IndexedBytes<'a>) -> Result<'a, u16> {
let mut port_num: u32 = 0;
let source = Some(input.cow_source());
let string = bytes.from_cow_source(&source);
for (&b, i) in string.iter().rev().zip(&[1, 10, 100, 1000, 10000]) {
if b < b'0' || b > b'9' {
return Err(pear_error!("port byte is out of range"));
}
port_num += (b - b'0') as u32 * i;
}
if port_num > u16::max_value() as u32 {
return Err(pear_error!("port value out of range: {}", port_num));
}
Ok(port_num as u16)
}
#[parser]
fn port<'a>(input: &mut RawInput<'a>) -> Result<'a, u16> {
let port_str = take_n_while(5, |c| c >= b'0' && c <= b'9')?;
port_from(&port_str)?
}
#[parser]
fn authority<'a>(
input: &mut RawInput<'a>,
user_info: Option<IndexedBytes<'a>>
) -> Result<'a, Authority<'a>> {
let host = switch! {
peek(b'[') => Host::Bracketed(delimited(b'[', is_pchar, b']')?),
_ => Host::Raw(take_while(is_reg_name_char)?)
};
let port = pear_try!(eat(b':') => port()?);
unsafe { Authority::raw(input.cow_source(), user_info, host, port) }
}
#[parser]
fn absolute<'a>(
input: &mut RawInput<'a>,
scheme: IndexedBytes<'a>
) -> Result<'a, Absolute<'a>> {
let (authority, path_and_query) = switch! {
eat_slice(b"://") => {
let left = take_while(|c| is_reg_name_char(c) || c == b':')?;
let authority = switch! {
eat(b'@') => authority(Some(left))?,
_ => {
input.backtrack(left.len())?;
authority(None)?
}
};
let path_and_query = pear_try!(path_and_query(is_pchar));
(Some(authority), path_and_query)
},
eat(b':') => (None, Some(path_and_query(is_pchar)?)),
_ => return Err(pear_error!("expected ':' but none was found"))
};
unsafe { Absolute::raw(input.cow_source(), scheme, authority, path_and_query) }
}
#[parser]
crate fn authority_only<'a>(input: &mut RawInput<'a>) -> Result<'a, Authority<'a>> {
if let Uri::Authority(authority) = absolute_or_authority()? {
Ok(authority)
} else {
Err(pear_error!("expected authority URI but found absolute URI"))
}
}
#[parser]
crate fn absolute_only<'a>(input: &mut RawInput<'a>) -> Result<'a, Absolute<'a>> {
if let Uri::Absolute(absolute) = absolute_or_authority()? {
Ok(absolute)
} else {
Err(pear_error!("expected absolute URI but found authority URI"))
}
}
#[parser]
fn absolute_or_authority<'a>(
input: &mut RawInput<'a>,
) -> Result<'a, Uri<'a>> {
let left = take_while(is_reg_name_char)?;
switch! {
peek_slice(b":/") => Uri::Absolute(absolute(left)?),
eat(b'@') => Uri::Authority(authority(Some(left))?),
colon@take_n_if(1, |b| b == b':') => {
let rest = take_while(|c| is_reg_name_char(c) || c == b':')?;
switch! {
eat(b'@') => Uri::Authority(authority(Some(left + colon + rest))?),
peek(b'/') => {
input.backtrack(rest.len() + 1)?;
Uri::Absolute(absolute(left)?)
},
_ => unsafe {
let query = pear_try!(eat(b'?') => take_while(is_pchar)?);
if query.is_some() || rest.is_empty() || rest.len() > 5 {
Uri::raw_absolute(input.cow_source(), left, rest, query)
} else if let Ok(port) = port_from(input, &rest) {
let host = Host::Raw(left);
let source = input.cow_source();
let port = Some(port);
Uri::Authority(Authority::raw(source, None, host, port))
} else {
Uri::raw_absolute(input.cow_source(), left, rest, query)
}
}
}
},
_ => {
input.backtrack(left.len())?;
Uri::Authority(authority(None)?)
}
}
}