Update Pear to 0.2.

This commit is contained in:
Sergio Benitez 2020-07-16 05:46:20 -07:00
parent 27b26188c4
commit 95a4b442cc
20 changed files with 204 additions and 330 deletions

View File

@ -175,10 +175,7 @@ impl FromMeta for Origin {
let uri = http::uri::Origin::parse_route(&string)
.map_err(|e| {
let span = e.index()
.map(|i| string.subspan(i + 1..))
.unwrap_or(string.span());
let span = string.subspan(e.index() + 1..);
span.error(format!("invalid path URI: {}", e))
.help("expected path in origin form: \"/path/<param>\"")
})?;

View File

@ -1,4 +1,4 @@
error: invalid path URI: expected token '/' but found 'a' at index 0
error: invalid path URI: expected token / but found a at index 0
--> $DIR/route-path-bad-syntax.rs:5:8
|
5 | #[get("a")] //~ ERROR invalid path URI
@ -6,7 +6,7 @@ error: invalid path URI: expected token '/' but found 'a' at index 0
|
= help: expected path in origin form: "/path/<param>"
error: invalid path URI: expected token '/' but none was found at index 0
error: invalid path URI: expected token / but none was found at index 0
--> $DIR/route-path-bad-syntax.rs:9:8
|
9 | #[get("")] //~ ERROR invalid path URI
@ -14,7 +14,7 @@ error: invalid path URI: expected token '/' but none was found at index 0
|
= help: expected path in origin form: "/path/<param>"
error: invalid path URI: expected token '/' but found 'a' at index 0
error: invalid path URI: expected token / but found a at index 0
--> $DIR/route-path-bad-syntax.rs:13:8
|
13 | #[get("a/b/c")] //~ ERROR invalid path URI
@ -50,7 +50,7 @@ error: paths cannot contain empty segments
|
= note: expected '/a/b', found '/a/b//'
error: invalid path URI: expected EOF but found '#' at index 3
error: invalid path URI: expected EOF but found # at index 3
--> $DIR/route-path-bad-syntax.rs:33:11
|
33 | #[get("/!@#$%^&*()")] //~ ERROR invalid path URI

View File

@ -31,10 +31,13 @@ state = "0.4"
tokio-rustls = { version = "0.14.0", optional = true }
tokio = { version = "0.2.9", features = ["sync", "tcp", "time"] }
cookie = { version = "0.14.0", features = ["percent-encode"] }
pear = "0.1"
unicode-xid = "0.2"
log = "0.4"
ref-cast = "1.0"
[dependencies.pear]
git = "https://github.com/SergioBenitez/Pear.git"
rev = "faee7c7e"
[dev-dependencies]
rocket = { version = "0.5.0-dev", path = "../lib" }

View File

@ -88,17 +88,17 @@ pub enum AcceptParams {
Dynamic(SmallVec<[QMediaType; 1]>)
}
impl pear::parsers::Collection for AcceptParams {
type Item = QMediaType;
fn new() -> Self {
impl Default for AcceptParams {
fn default() -> Self {
AcceptParams::Dynamic(SmallVec::new())
}
}
fn add(&mut self, item: Self::Item) {
match *self {
impl Extend<QMediaType> for AcceptParams {
fn extend<T: IntoIterator<Item = QMediaType>>(&mut self, iter: T) {
match self {
AcceptParams::Static(..) => panic!("can't add to static collection!"),
AcceptParams::Dynamic(ref mut v) => v.push(item)
AcceptParams::Dynamic(ref mut v) => v.extend(iter)
}
}
}

View File

@ -22,21 +22,35 @@ pub enum MediaParams {
Dynamic(SmallVec<[(IndexedString, IndexedString); 2]>)
}
impl pear::parsers::Collection for MediaParams {
type Item = (IndexedString, IndexedString);
fn new() -> Self {
impl Default for MediaParams {
fn default() -> Self {
MediaParams::Dynamic(SmallVec::new())
}
}
fn add(&mut self, item: Self::Item) {
match *self {
impl Extend<(IndexedString, IndexedString)> for MediaParams {
fn extend<T: IntoIterator<Item = (IndexedString, IndexedString)>>(&mut self, iter: T) {
match self {
MediaParams::Static(..) => panic!("can't add to static collection!"),
MediaParams::Dynamic(ref mut v) => v.push(item)
MediaParams::Dynamic(ref mut v) => v.extend(iter)
}
}
}
impl PartialEq for MediaParams {
fn eq(&self, other: &MediaParams) -> bool {
#[inline(always)]
fn inner_types(params: &MediaParams) -> &[(IndexedString, IndexedString)] {
match *params {
MediaParams::Static(params) => params,
MediaParams::Dynamic(ref vec) => vec,
}
}
inner_types(self) == inner_types(other)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Source {
Known(&'static str),

View File

@ -1,22 +1,22 @@
use pear::parser;
use pear::parsers::*;
use pear::macros::{parser, parse_error};
use pear::combinators::{series, surrounded};
use crate::{Accept, QMediaType};
use crate::parse::checkers::is_whitespace;
use crate::parse::media_type::media_type;
type Input<'a> = crate::parse::IndexedInput<'a, str>;
type Result<'a, T> = pear::Result<T, Input<'a>>;
type Input<'a> = pear::input::Pear<pear::input::Cursor<&'a str>>;
type Result<'a, T> = pear::input::Result<T, Input<'a>>;
#[parser]
fn weighted_media_type<'a>(input: &mut Input<'a>) -> Result<'a, QMediaType> {
let media_type = media_type()?;
let weight = match media_type.params().next() {
Some(("q", value)) if value.len() <= 5 => match value.parse::<f32>().ok() {
Some(q) if q > 1. => return Err(pear_error!("q value must be <= 1")),
Some(q) if q < 0. => return Err(pear_error!("q value must be > 0")),
Some(q) if q > 1. => parse_error!("q value must be <= 1")?,
Some(q) if q < 0. => parse_error!("q value must be > 0")?,
Some(q) => Some(q),
None => return Err(pear_error!("invalid media-type weight"))
None => parse_error!("invalid media-type weight")?
},
_ => None
};
@ -26,11 +26,11 @@ fn weighted_media_type<'a>(input: &mut Input<'a>) -> Result<'a, QMediaType> {
#[parser]
fn accept<'a>(input: &mut Input<'a>) -> Result<'a, Accept> {
Accept(series(false, ',', is_whitespace, weighted_media_type)?)
Accept(series(|i| surrounded(i, weighted_media_type, is_whitespace), ',')?)
}
pub fn parse_accept(input: &str) -> Result<'_, Accept> {
parse!(accept: &mut input.into())
parse!(accept: Input::new(input))
}
#[cfg(test)]

View File

@ -1,10 +1,10 @@
#[inline(always)]
pub fn is_whitespace(byte: char) -> bool {
pub fn is_whitespace(&byte: &char) -> bool {
byte == ' ' || byte == '\t'
}
#[inline]
pub fn is_valid_token(c: char) -> bool {
pub fn is_valid_token(&c: &char) -> bool {
match c {
'0'..='9' | 'A'..='Z' | '^'..='~' | '#'..='\''
| '!' | '*' | '+' | '-' | '.' => true,

View File

@ -4,10 +4,12 @@ use std::borrow::Cow;
use std::ops::{Index, Range};
use std::fmt::{self, Debug};
use pear::{Input, Length};
use pear::input::Length;
use crate::ext::IntoOwned;
pub use pear::input::Extent;
pub type IndexedString = Indexed<'static, str>;
pub type IndexedStr<'a> = Indexed<'a, str>;
pub type IndexedBytes<'a> = Indexed<'a, [u8]>;
@ -36,9 +38,15 @@ pub enum Indexed<'a, T: ?Sized + ToOwned> {
Concrete(Cow<'a, T>)
}
impl<'a, T: ?Sized + ToOwned + 'a, C: Into<Cow<'a, T>>> From<C> for Indexed<'a, T> {
impl<A, T: ?Sized + ToOwned> From<Extent<A>> for Indexed<'_, T> {
fn from(e: Extent<A>) -> Self {
Indexed::Indexed(e.start, e.end)
}
}
impl<'a, T: ?Sized + ToOwned + 'a> From<Cow<'a, T>> for Indexed<'a, T> {
#[inline(always)]
fn from(value: C) -> Indexed<'a, T> {
fn from(value: Cow<'a, T>) -> Indexed<'a, T> {
Indexed::Concrete(value.into())
}
}
@ -213,130 +221,3 @@ impl<'a, T: ?Sized + Length + ToOwned + 'a> Length for Indexed<'a, T> {
}
}
}
#[derive(Debug)]
pub struct IndexedInput<'a, T: ?Sized> {
source: &'a T,
current: &'a T
}
impl<'a, T: ?Sized + 'a> IndexedInput<'a, T> {
#[inline(always)]
pub fn source(&self) -> &T {
self.source
}
}
impl<'a, T: ToOwned + ?Sized + 'a> IndexedInput<'a, T> {
#[inline(always)]
pub fn cow_source(&self) -> Cow<'a, T> {
Cow::Borrowed(self.source)
}
}
impl IndexedInput<'_, [u8]> {
pub fn backtrack(&mut self, n: usize) -> pear::Result<(), Self> {
let source_addr = self.source.as_ptr() as usize;
let current_addr = self.current.as_ptr() as usize;
if current_addr > n && (current_addr - n) >= source_addr {
let forward = (current_addr - n) - source_addr;
self.current = &self.source[forward..];
Ok(())
} else {
let diag = format!("({}, {:x} in {:x})", n, current_addr, source_addr);
Err(pear_error!([backtrack; self] "internal error: {}", diag))
}
}
pub fn len(&self) -> usize {
self.source.len()
}
}
macro_rules! impl_indexed_input {
($T:ty, token = $token:ty) => (
impl<'a> From<&'a $T> for IndexedInput<'a, $T> {
#[inline(always)]
fn from(source: &'a $T) -> Self {
IndexedInput { source: source, current: source }
}
}
impl<'a> Input for IndexedInput<'a, $T> {
type Token = $token;
type InSlice = &'a $T;
type Slice = Indexed<'static, $T>;
type Many = Indexed<'static, $T>;
type Context = Context;
#[inline(always)]
fn peek(&mut self) -> Option<Self::Token> {
self.current.peek()
}
#[inline(always)]
fn peek_slice(&mut self, slice: Self::InSlice) -> Option<Self::Slice> {
self.current.peek_slice(slice)
.map(|slice| unsafe {
Indexed::unchecked_from(slice, self.source)
})
}
#[inline(always)]
fn skip_many<F>(&mut self, cond: F) -> usize
where F: FnMut(Self::Token) -> bool
{
self.current.skip_many(cond)
}
#[inline(always)]
fn take_many<F>(&mut self, cond: F) -> Self::Many
where F: FnMut(Self::Token) -> bool
{
let many = self.current.take_many(cond);
unsafe { Indexed::unchecked_from(many, self.source) }
}
#[inline(always)]
fn advance(&mut self, count: usize) {
self.current.advance(count)
}
#[inline(always)]
fn is_empty(&mut self) -> bool {
self.current.is_empty()
}
fn context(&mut self) -> Option<Context> {
let offset = self.source.len() - self.current.len();
let bytes: &[u8] = self.current.as_ref();
let string = String::from_utf8(bytes.into()).ok()?;
Some(Context { offset, string })
}
}
)
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct Context {
pub offset: usize,
pub string: String
}
impl std::fmt::Display for Context {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
const LIMIT: usize = 7;
write!(f, "[{}:]", self.offset)?;
if self.string.len() > LIMIT {
write!(f, " {}..", &self.string[..LIMIT])
} else if !self.string.is_empty() {
write!(f, " {}", &self.string)
} else {
Ok(())
}
}
}
impl_indexed_input!([u8], token = u8);
impl_indexed_input!(str, token = char);

View File

@ -1,21 +1,22 @@
use std::borrow::Cow;
use pear::{parser, switch};
use pear::input::Extent;
use pear::combinators::{prefixed_series, surrounded};
use pear::macros::{parser, switch, parse};
use pear::parsers::*;
use crate::media_type::{MediaType, Source};
use crate::parse::checkers::{is_whitespace, is_valid_token};
use crate::parse::IndexedStr;
type Input<'a> = crate::parse::IndexedInput<'a, str>;
type Result<'a, T> = pear::Result<T, Input<'a>>;
type Input<'a> = pear::input::Pear<pear::input::Cursor<&'a str>>;
type Result<'a, T> = pear::input::Result<T, Input<'a>>;
#[parser]
fn quoted_string<'a>(input: &mut Input<'a>) -> Result<'a, IndexedStr<'a>> {
fn quoted_string<'a>(input: &mut Input<'a>) -> Result<'a, Extent<&'a str>> {
eat('"')?;
let mut is_escaped = false;
let inner = take_while(|c| {
let inner = take_while(|&c| {
if is_escaped { is_escaped = false; return true; }
if c == '\\' { is_escaped = true; return true; }
c != '"'
@ -26,7 +27,7 @@ fn quoted_string<'a>(input: &mut Input<'a>) -> Result<'a, IndexedStr<'a>> {
}
#[parser]
fn media_param<'a>(input: &mut Input<'a>) -> Result<'a, (IndexedStr<'a>, IndexedStr<'a>)> {
fn media_param<'a>(input: &mut Input<'a>) -> Result<'a, (Extent<&'a str>, Extent<&'a str>)> {
let key = (take_some_while_until(is_valid_token, '=')?, eat('=')?).0;
let value = switch! {
peek('"') => quoted_string()?,
@ -41,21 +42,24 @@ pub fn media_type<'a>(input: &mut Input<'a>) -> Result<'a, MediaType> {
let (top, sub, params) = {
let top = (take_some_while_until(is_valid_token, '/')?, eat('/')?).0;
let sub = take_some_while_until(is_valid_token, ';')?;
let params = series(true, ';', is_whitespace, |i| {
media_param(i).map(|(k, v)| (k.coerce_lifetime(), v.coerce_lifetime()))
})?;
let params = prefixed_series(';', |i| {
let param = surrounded(i, media_param, is_whitespace)?;
Ok((param.0.into(), param.1.into()))
}, ';')?;
(top.coerce_lifetime(), sub.coerce_lifetime(), params)
(top, sub, params)
};
MediaType {
source: Source::Custom(Cow::Owned(input.source().to_string())),
top, sub, params
params,
source: Source::Custom(Cow::Owned(input.start.to_string())),
top: top.into(),
sub: sub.into(),
}
}
pub fn parse_media_type(input: &str) -> Result<'_, MediaType> {
parse!(media_type: &mut input.into())
parse!(media_type: Input::new(input))
}
#[cfg(test)]

View File

@ -1,8 +1,8 @@
use std::fmt;
use std::borrow::Cow;
use pear::{ParseErr, Expected};
use crate::parse::indexed::Context;
use pear::error::Expected;
use pear::input::ParseError;
use crate::parse::uri::RawInput;
use crate::ext::IntoOwned;
@ -14,32 +14,18 @@ use crate::ext::IntoOwned;
/// `Display` implementation. In other words, by printing a value of this type.
#[derive(Debug)]
pub struct Error<'a> {
expected: Expected<Or<char, u8>, Cow<'a, str>, String>,
context: Option<Context>
expected: Expected<u8, Cow<'a, [u8]>>,
index: usize,
}
#[derive(Debug)]
enum Or<L, R> {
A(L),
B(R)
}
impl<'a> Error<'a> {
pub(crate) fn from(src: &'a str, pear_error: ParseErr<RawInput<'a>>) -> Error<'a> {
let new_expected = pear_error.expected.map(|token| {
if token.is_ascii() && !token.is_ascii_control() {
Or::A(token as char)
} else {
Or::B(token)
}
}, String::from_utf8_lossy, |indexed| {
let src = Some(src.as_bytes());
String::from_utf8_lossy(indexed.from_source(src)).to_string()
});
Error { expected: new_expected, context: pear_error.context }
impl<'a> From<ParseError<RawInput<'a>>> for Error<'a> {
fn from(inner: ParseError<RawInput<'a>>) -> Self {
let expected = inner.error.map(|t| t.into(), |v| v.values.into());
Error { expected, index: inner.info.context.start }
}
}
impl Error<'_> {
/// Returns the byte index into the text where the error occurred if it is
/// known.
///
@ -50,41 +36,27 @@ impl<'a> Error<'a> {
/// use rocket::http::uri::Origin;
///
/// let err = Origin::parse("/foo bar").unwrap_err();
/// assert_eq!(err.index(), Some(4));
/// assert_eq!(err.index(), 4);
/// ```
pub fn index(&self) -> Option<usize> {
self.context.as_ref().map(|c| c.offset)
}
}
impl fmt::Display for Or<char, u8> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Or::A(left) => write!(f, "'{}'", left),
Or::B(right) => write!(f, "non-ASCII byte {}", right),
}
pub fn index(&self) -> usize {
self.index
}
}
impl fmt::Display for Error<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// This relies on specialization of the `Display` impl for `Expected`.
write!(f, "{}", self.expected)?;
if let Some(ref context) = self.context {
write!(f, " at index {}", context.offset)?;
}
Ok(())
write!(f, "{} at index {}", self.expected, self.index)
}
}
impl IntoOwned for Error<'_> {
type Owned = Error<'static>;
fn into_owned(self) -> Self::Owned {
let expected = self.expected.map(|i| i, IntoOwned::into_owned, |i| i);
Error { expected, context: self.context }
fn into_owned(self) -> Error<'static> {
Error {
expected: self.expected.map(|t| t, |s| s.into_owned().into()),
index: self.index
}
}
}
@ -101,8 +73,8 @@ mod tests {
#[test]
fn check_display() {
check_err!("a" => "expected token '/' but found 'a' at index 0");
check_err!("?" => "expected token '/' but found '?' at index 0");
check_err!("" => "expected token '/' but found non-ASCII byte 232 at index 0");
check_err!("a" => "expected token / but found a at index 0");
check_err!("?" => "expected token / but found ? at index 0");
check_err!("" => "expected token / but found byte 232 at index 0");
}
}

View File

@ -5,40 +5,35 @@ mod tables;
#[cfg(test)] mod tests;
use crate::uri::{Uri, Origin, Absolute, Authority};
use crate::parse::indexed::IndexedInput;
use self::parser::{uri, origin, authority_only, absolute_only, rocket_route_origin};
pub use self::tables::{is_pchar, PATH_SET};
pub use self::error::Error;
type RawInput<'a> = IndexedInput<'a, [u8]>;
type RawInput<'a> = pear::input::Pear<pear::input::Cursor<&'a [u8]>>;
#[inline]
pub fn from_str(string: &str) -> Result<Uri<'_>, Error<'_>> {
parse!(uri: &mut RawInput::from(string.as_bytes()))
.map_err(|e| Error::from(string, e))
pub fn from_str(s: &str) -> Result<Uri<'_>, Error<'_>> {
Ok(parse!(uri: RawInput::new(s.as_bytes()))?)
}
#[inline]
pub fn origin_from_str(string: &str) -> Result<Origin<'_>, Error<'_>> {
parse!(origin: &mut RawInput::from(string.as_bytes()))
.map_err(|e| Error::from(string, e))
pub fn origin_from_str(s: &str) -> Result<Origin<'_>, Error<'_>> {
Ok(parse!(origin: RawInput::new(s.as_bytes()))?)
}
#[inline]
pub fn route_origin_from_str(string: &str) -> Result<Origin<'_>, Error<'_>> {
parse!(rocket_route_origin: &mut RawInput::from(string.as_bytes()))
.map_err(|e| Error::from(string, e))
pub fn route_origin_from_str(s: &str) -> Result<Origin<'_>, Error<'_>> {
Ok(parse!(rocket_route_origin: RawInput::new(s.as_bytes()))?)
}
#[inline]
pub fn authority_from_str(string: &str) -> Result<Authority<'_>, Error<'_>> {
parse!(authority_only: &mut RawInput::from(string.as_bytes()))
.map_err(|e| Error::from(string, e))
pub fn authority_from_str(s: &str) -> Result<Authority<'_>, Error<'_>> {
Ok(parse!(authority_only: RawInput::new(s.as_bytes()))?)
}
#[inline]
pub fn absolute_from_str(string: &str) -> Result<Absolute<'_>, Error<'_>> {
parse!(absolute_only: &mut RawInput::from(string.as_bytes()))
.map_err(|e| Error::from(string, e))
pub fn absolute_from_str(s: &str) -> Result<Absolute<'_>, Error<'_>> {
Ok(parse!(absolute_only: RawInput::new(s.as_bytes()))?)
}

View File

@ -1,24 +1,24 @@
use pear::parsers::*;
use pear::{parser, switch};
use pear::input::{Extent, Rewind};
use pear::macros::{parser, switch, parse_current_marker, parse_error, parse_try};
use crate::uri::{Uri, Origin, Authority, Absolute, Host};
use crate::parse::uri::tables::{is_reg_name_char, is_pchar, is_pchar_or_rchar};
use crate::parse::uri::RawInput;
use crate::parse::IndexedBytes;
type Result<'a, T> = pear::Result<T, RawInput<'a>>;
type Result<'a, T> = pear::input::Result<T, RawInput<'a>>;
#[parser]
pub fn uri<'a>(input: &mut RawInput<'a>) -> Result<'a, Uri<'a>> {
match input.len() {
0 => return Err(pear_error!("empty URI")),
match input.items.len() {
0 => parse_error!("empty URI")?,
1 => switch! {
eat(b'*') => Uri::Asterisk,
eat(b'/') => Uri::Origin(Origin::new::<_, &str>("/", None)),
_ => unsafe {
// the `is_reg_name_char` guarantees ASCII
let host = Host::Raw(take_n_if(1, is_reg_name_char)?);
Uri::Authority(Authority::raw(input.cow_source(), None, host, None))
Uri::Authority(Authority::raw(input.start.into(), None, host, None))
}
},
_ => switch! {
@ -40,39 +40,37 @@ pub fn rocket_route_origin<'a>(input: &mut RawInput<'a>) -> Result<'a, Origin<'a
#[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
where F: Fn(&u8) -> bool + Copy
{
let path = take_while(is_good_char)?;
// FIXME(rustc): We should be able to use `pear_try`, but rustc...is broken.
// FIXME: this works on nightly but not stable! `Span` issues?
// let query = parse_try!(eat(b'?') => take_while(|c| is_good_char(c) || *c == b'?')?);
let query = switch! {
eat(b'?') => Some(take_while(|c| is_good_char(c) || c == b'?')?),
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"))
parse_error!("expected path or query, found neither")?
} else {
// We know the string is ASCII because of the `is_good_char` checks above.
Ok(unsafe { Origin::raw(input.cow_source(), path, query) })
Ok(unsafe {Origin::raw(input.start.into(), path.into(), query.map(|q| q.into())) })
}
}
#[parser]
fn port_from<'a>(input: &mut RawInput<'a>, bytes: &IndexedBytes<'a>) -> Result<'a, u16> {
fn port_from<'a>(input: &mut RawInput<'a>, bytes: &[u8]) -> 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]) {
for (b, i) in bytes.iter().rev().zip(&[1, 10, 100, 1000, 10000]) {
if !b.is_ascii_digit() {
return Err(pear_error!("port byte is out of range"));
parse_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));
parse_error!("port out of range: {}", port_num)?;
}
Ok(port_num as u16)
@ -80,14 +78,14 @@ fn port_from<'a>(input: &mut RawInput<'a>, bytes: &IndexedBytes<'a>) -> Result<'
#[parser]
fn port<'a>(input: &mut RawInput<'a>) -> Result<'a, u16> {
let port_str = take_n_while(5, |c| c.is_ascii_digit())?;
port_from(&port_str)?
let port = take_n_while(5, |c| c.is_ascii_digit())?;
port_from(&port)?
}
#[parser]
fn authority<'a>(
input: &mut RawInput<'a>,
user_info: Option<IndexedBytes<'a>>
user_info: Option<Extent<&'a [u8]>>
) -> Result<'a, Authority<'a>> {
let host = switch! {
peek(b'[') => Host::Bracketed(delimited(b'[', is_pchar, b']')?),
@ -95,36 +93,37 @@ fn authority<'a>(
};
// The `is_pchar`,`is_reg_name_char`, and `port()` functions ensure ASCII.
let port = pear_try!(eat(b':') => port()?);
unsafe { Authority::raw(input.cow_source(), user_info, host, port) }
let port = parse_try!(eat(b':') => port()?);
unsafe { Authority::raw(input.start.into(), user_info, host, port) }
}
// Callers must ensure that `scheme` is actually ASCII.
#[parser]
fn absolute<'a>(
input: &mut RawInput<'a>,
scheme: IndexedBytes<'a>
scheme: Extent<&'a [u8]>
) -> 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 before_auth = parse_current_marker!();
let left = take_while(|c| is_reg_name_char(c) || *c == b':')?;
let authority = switch! {
eat(b'@') => authority(Some(left))?,
_ => {
input.backtrack(left.len())?;
input.rewind_to(before_auth);
authority(None)?
}
};
let path_and_query = pear_try!(path_and_query(is_pchar));
let path_and_query = parse_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"))
_ => parse_error!("expected ':' but none was found")?
};
// `authority` and `path_and_query` parsers ensure ASCII.
unsafe { Absolute::raw(input.cow_source(), scheme, authority, path_and_query) }
unsafe { Absolute::raw(input.start.into(), scheme, authority, path_and_query) }
}
#[parser]
@ -132,7 +131,7 @@ pub 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"))
parse_error!("expected authority URI but found absolute URI")?
}
}
@ -141,7 +140,7 @@ pub 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"))
parse_error!("expected absolute URI but found authority URI")?
}
}
@ -149,17 +148,20 @@ pub fn absolute_only<'a>(input: &mut RawInput<'a>) -> Result<'a, Absolute<'a>> {
fn absolute_or_authority<'a>(
input: &mut RawInput<'a>,
) -> Result<'a, Uri<'a>> {
let start = parse_current_marker!();
let left = take_while(is_reg_name_char)?;
let mark_at_left = parse_current_marker!();
switch! {
peek_slice(b":/") => Uri::Absolute(absolute(left)?),
eat(b'@') => Uri::Authority(authority(Some(left))?),
colon@take_n_if(1, |b| b == b':') => {
take_n_if(1, |b| *b == b':') => {
// could be authority or an IP with ':' in it
let rest = take_while(|c| is_reg_name_char(c) || c == b':')?;
let rest = take_while(|c| is_reg_name_char(c) || *c == b':')?;
let authority_here = parse_context!();
switch! {
eat(b'@') => Uri::Authority(authority(Some(left + colon + rest))?),
eat(b'@') => Uri::Authority(authority(Some(authority_here))?),
peek(b'/') => {
input.backtrack(rest.len() + 1)?;
input.rewind_to(mark_at_left);
Uri::Absolute(absolute(left)?)
},
_ => unsafe {
@ -168,22 +170,22 @@ fn absolute_or_authority<'a>(
// parses. To settle the ambiguity, we assume that if it
// looks like a port, it's a port. Otherwise a host. Unless
// we have a query, in which case it's definitely a host.
let query = pear_try!(eat(b'?') => take_while(is_pchar)?);
let query = parse_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)
Uri::raw_absolute(input.start.into(), left, rest, query)
} else if let Ok(port) = port_from(input, &rest) {
let host = Host::Raw(left);
let source = input.cow_source();
let source = input.start.into();
let port = Some(port);
Uri::Authority(Authority::raw(source, None, host, port))
} else {
Uri::raw_absolute(input.cow_source(), left, rest, query)
Uri::raw_absolute(input.start.into(), left, rest, query)
}
}
}
},
_ => {
input.backtrack(left.len())?;
input.rewind_to(start);
Uri::Authority(authority(None)?)
}
}

View File

@ -68,12 +68,12 @@ pub const PATH_SET: AsciiSet;
}
#[inline(always)]
pub fn is_pchar(c: u8) -> bool {
pub fn is_pchar(&c: &u8) -> bool {
PATH_CHARS[c as usize] == c
}
#[inline(always)]
pub fn is_pchar_or_rchar(c: u8) -> bool {
pub fn is_pchar_or_rchar(&c: &u8) -> bool {
PATH_CHARS[c as usize] != 0
}
@ -108,7 +108,7 @@ const REG_CHARS: [u8; 256] = [
];
#[inline(always)]
pub fn is_reg_name_char(c: u8) -> bool {
pub fn is_reg_name_char(&c: &u8) -> bool {
REG_CHARS[c as usize] != 0
}

View File

@ -2,7 +2,7 @@ use std::borrow::Cow;
use std::fmt::{self, Display};
use crate::ext::IntoOwned;
use crate::parse::{Indexed, IndexedStr};
use crate::parse::{Extent, IndexedStr};
use crate::uri::{Authority, Origin, Error, as_utf8_unchecked};
/// A URI with a scheme, authority, path, and query:
@ -46,15 +46,14 @@ impl<'a> Absolute<'a> {
#[inline]
pub(crate) unsafe fn raw(
source: Cow<'a, [u8]>,
scheme: Indexed<'a, [u8]>,
scheme: Extent<&'a [u8]>,
authority: Option<Authority<'a>>,
origin: Option<Origin<'a>>,
) -> Absolute<'a> {
Absolute {
authority, origin,
source: Some(as_utf8_unchecked(source)),
scheme: scheme.coerce(),
authority: authority,
origin: origin,
scheme: scheme.into(),
}
}
@ -65,7 +64,9 @@ impl<'a> Absolute<'a> {
origin: Option<Origin<'a>>
) -> Absolute<'a> {
Absolute {
source: None, scheme: scheme.into(), authority, origin
authority, origin,
source: None,
scheme: Cow::Borrowed(scheme).into(),
}
}

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Display};
use std::borrow::Cow;
use crate::ext::IntoOwned;
use crate::parse::{Indexed, IndexedStr};
use crate::parse::{Extent, IndexedStr};
use crate::uri::{as_utf8_unchecked, Error};
/// A URI with an authority only: `user:pass@host:8000`.
@ -57,14 +57,14 @@ impl IntoOwned for Authority<'_> {
impl<'a> Authority<'a> {
pub(crate) unsafe fn raw(
source: Cow<'a, [u8]>,
user_info: Option<Indexed<'a, [u8]>>,
host: Host<Indexed<'a, [u8]>>,
user_info: Option<Extent<&'a [u8]>>,
host: Host<Extent<&'a [u8]>>,
port: Option<u16>
) -> Authority<'a> {
Authority {
source: Some(as_utf8_unchecked(source)),
user_info: user_info.map(|u| u.coerce()),
host: host.map_inner(|inner| inner.coerce()),
user_info: user_info.map(IndexedStr::from),
host: host.map_inner(IndexedStr::from),
port: port
}
}
@ -77,8 +77,8 @@ impl<'a> Authority<'a> {
) -> Authority<'a> {
Authority {
source: None,
user_info: user_info.map(|u| u.into()),
host: host.map_inner(|inner| inner.into()),
user_info: user_info.map(|u| Cow::Borrowed(u).into()),
host: host.map_inner(|inner| Cow::Borrowed(inner).into()),
port: port
}
}

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Display};
use std::borrow::Cow;
use crate::ext::IntoOwned;
use crate::parse::{Indexed, IndexedStr};
use crate::parse::{Indexed, Extent, IndexedStr};
use crate::uri::{as_utf8_unchecked, Error, Segments};
use state::Storage;
@ -114,13 +114,13 @@ impl<'a> Origin<'a> {
#[inline]
pub(crate) unsafe fn raw(
source: Cow<'a, [u8]>,
path: Indexed<'a, [u8]>,
query: Option<Indexed<'a, [u8]>>
path: Extent<&'a [u8]>,
query: Option<Extent<&'a [u8]>>
) -> Origin<'a> {
Origin {
source: Some(as_utf8_unchecked(source)),
path: path.coerce(),
query: query.map(|q| q.coerce()),
path: path.into(),
query: query.map(|q| q.into()),
segment_count: Storage::new()
}
}
@ -134,8 +134,8 @@ impl<'a> Origin<'a> {
{
Origin {
source: None,
path: Indexed::from(path),
query: query.map(Indexed::from),
path: Indexed::from(path.into()),
query: query.map(|q| Indexed::from(q.into())),
segment_count: Storage::new()
}
}
@ -340,13 +340,13 @@ impl<'a> Origin<'a> {
#[inline]
pub fn map_path<F: FnOnce(&str) -> String>(&self, f: F) -> Option<Self> {
let path = f(self.path());
if !path.starts_with('/') || !path.bytes().all(crate::parse::uri::is_pchar) {
if !path.starts_with('/') || !path.bytes().all(|b| crate::parse::uri::is_pchar(&b)) {
return None;
}
Some(Origin {
source: self.source.clone(),
path: path.into(),
path: Cow::from(path).into(),
query: self.query.clone(),
segment_count: Storage::new(),
})

View File

@ -5,7 +5,7 @@ use std::str::Utf8Error;
use std::convert::TryFrom;
use crate::ext::IntoOwned;
use crate::parse::Indexed;
use crate::parse::Extent;
use crate::uri::{Origin, Authority, Absolute, Error};
use crate::uri::encoding::{percent_encode, DEFAULT_ENCODE_SET};
@ -64,9 +64,9 @@ impl<'a> Uri<'a> {
#[inline]
pub(crate) unsafe fn raw_absolute(
source: Cow<'a, [u8]>,
scheme: Indexed<'a, [u8]>,
path: Indexed<'a, [u8]>,
query: Option<Indexed<'a, [u8]>>,
scheme: Extent<&'a [u8]>,
path: Extent<&'a [u8]>,
query: Option<Extent<&'a [u8]>>,
) -> Uri<'a> {
let origin = Origin::raw(source.clone(), path, query);
Uri::Absolute(Absolute::raw(source.clone(), scheme, None, Some(origin)))

View File

@ -35,12 +35,15 @@ state = "0.4.1"
time = "0.2.11"
memchr = "2" # TODO: Use pear instead.
binascii = "0.1"
pear = "0.1"
atty = "0.2"
async-trait = "0.1"
ref-cast = "1.0"
atomic = "0.4"
[dependencies.pear]
git = "https://github.com/SergioBenitez/Pear.git"
rev = "faee7c7e"
[dependencies.tokio]
version = "0.2.9"
features = ["fs", "io-std", "io-util", "rt-threaded", "sync", "signal", "macros"]

View File

@ -3,17 +3,20 @@ use std::result::Result as StdResult;
use crate::config::Value;
use pear::{Result, parser, switch};
use pear::macros::{parse, parser, switch};
use pear::parsers::*;
use pear::combinators::*;
type Input<'a> = pear::input::Pear<&'a str>;
type Result<'a, T> = pear::input::Result<T, Input<'a>>;
#[inline(always)]
pub fn is_whitespace(byte: char) -> bool {
byte == ' ' || byte == '\t'
pub fn is_whitespace(&byte: &char) -> bool {
byte.is_ascii_whitespace()
}
#[inline(always)]
fn is_not_separator(byte: char) -> bool {
fn is_not_separator(&byte: &char) -> bool {
match byte {
',' | '{' | '}' | '[' | ']' => false,
_ => true
@ -22,33 +25,33 @@ fn is_not_separator(byte: char) -> bool {
// FIXME: Be more permissive here?
#[inline(always)]
fn is_ident_char(byte: char) -> bool {
fn is_ident_char(&byte: &char) -> bool {
byte.is_ascii_alphanumeric() || byte == '_' || byte == '-'
}
#[parser]
fn array<'a>(input: &mut &'a str) -> Result<Value, &'a str> {
Value::Array(collection('[', value, ',', ']')?)
fn array<'a>(input: &mut Input<'a>) -> Result<'a, Value> {
Value::Array(delimited_collect('[', value, ',', ']')?)
}
#[parser]
fn key<'a>(input: &mut &'a str) -> Result<String, &'a str> {
fn key<'a>(input: &mut Input<'a>) -> Result<'a, String> {
take_some_while(is_ident_char)?.to_string()
}
#[parser]
fn key_value<'a>(input: &mut &'a str) -> Result<(String, Value), &'a str> {
fn key_value<'a>(input: &mut Input<'a>) -> Result<'a, (String, Value)> {
let key = (surrounded(key, is_whitespace)?, eat('=')?).0.to_string();
(key, surrounded(value, is_whitespace)?)
}
#[parser]
fn table<'a>(input: &mut &'a str) -> Result<Value, &'a str> {
Value::Table(collection('{', key_value, ',', '}')?)
fn table<'a>(input: &mut Input<'a>) -> Result<'a, Value> {
Value::Table(delimited_collect('{', key_value, ',', '}')?)
}
#[parser]
fn value<'a>(input: &mut &'a str) -> Result<Value, &'a str> {
fn value<'a>(input: &mut Input<'a>) -> Result<'a, Value> {
skip_while(is_whitespace)?;
let val = switch! {
eat_slice("true") => Value::Boolean(true),
@ -72,8 +75,8 @@ fn value<'a>(input: &mut &'a str) -> Result<Value, &'a str> {
val
}
pub fn parse_simple_toml_value(mut input: &str) -> StdResult<Value, String> {
parse!(value: &mut input).map_err(|e| e.to_string())
pub fn parse_simple_toml_value(input: &str) -> StdResult<Value, String> {
parse!(value: input).map_err(|e| e.to_string())
}
/// A simple wrapper over a `Value` reference with a custom implementation of

View File

@ -90,7 +90,6 @@ pub use rocket_codegen::*;
pub use async_trait::*;
#[macro_use] extern crate log;
#[macro_use] extern crate pear;
pub use futures;
pub use tokio;