mirror of https://github.com/rwf2/Rocket.git
Update to Pear 0.1.
This commit is contained in:
parent
397a646dcf
commit
900e716ea6
|
@ -29,10 +29,10 @@ fn media_type_to_expr(ecx: &ExtCtxt, ct: Option<MediaType>) -> Option<P<Expr>> {
|
||||||
let (top, sub) = (ct.top().as_str(), ct.sub().as_str());
|
let (top, sub) = (ct.top().as_str(), ct.sub().as_str());
|
||||||
quote_expr!(ecx, ::rocket::http::MediaType {
|
quote_expr!(ecx, ::rocket::http::MediaType {
|
||||||
source: ::rocket::http::Source::None,
|
source: ::rocket::http::Source::None,
|
||||||
top: ::rocket::http::IndexedStr::Concrete(
|
top: ::rocket::http::Indexed::Concrete(
|
||||||
::std::borrow::Cow::Borrowed($top)
|
::std::borrow::Cow::Borrowed($top)
|
||||||
),
|
),
|
||||||
sub: ::rocket::http::IndexedStr::Concrete(
|
sub: ::rocket::http::Indexed::Concrete(
|
||||||
::std::borrow::Cow::Borrowed($sub)
|
::std::borrow::Cow::Borrowed($sub)
|
||||||
),
|
),
|
||||||
params: ::rocket::http::MediaParams::Static(&[])
|
params: ::rocket::http::MediaParams::Static(&[])
|
||||||
|
|
|
@ -29,8 +29,7 @@ time = "0.1"
|
||||||
memchr = "2"
|
memchr = "2"
|
||||||
base64 = "0.9"
|
base64 = "0.9"
|
||||||
smallvec = "0.6"
|
smallvec = "0.6"
|
||||||
pear = { git = "http://github.com/SergioBenitez/pear", rev = "a96a7fd" }
|
pear = { git = "http://github.com/SergioBenitez/Pear", rev = "54667ae" }
|
||||||
pear_codegen = { git = "http://github.com/SergioBenitez/pear", rev = "a96a7fd" }
|
|
||||||
rustls = { version = "0.12.0", optional = true }
|
rustls = { version = "0.12.0", optional = true }
|
||||||
hyper = { version = "0.10.13", default-features = false }
|
hyper = { version = "0.10.13", default-features = false }
|
||||||
indexmap = "1.0"
|
indexmap = "1.0"
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::collections::BTreeMap;
|
use std::result::Result as StdResult;
|
||||||
|
|
||||||
use config::Value;
|
use config::Value;
|
||||||
|
|
||||||
use pear::{ParseResult, ParseError};
|
use pear::{Result, parser, switch};
|
||||||
use pear::parsers::*;
|
use pear::parsers::*;
|
||||||
use pear::combinators::*;
|
use pear::combinators::*;
|
||||||
|
|
||||||
|
@ -30,43 +30,37 @@ fn is_ident_char(byte: char) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[parser]
|
#[parser]
|
||||||
fn array<'a>(input: &mut &'a str) -> ParseResult<&'a str, Value> {
|
fn array<'a>(input: &mut &'a str) -> Result<Value, &'a str> {
|
||||||
let array = (eat('['), collect!(value(), eat(',')), eat(']')).1;
|
Value::Array(collection('[', value, ',', ']')?)
|
||||||
Value::Array(array)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[parser]
|
#[parser]
|
||||||
fn key<'a>(input: &mut &'a str) -> ParseResult<&'a str, String> {
|
fn key<'a>(input: &mut &'a str) -> Result<String, &'a str> {
|
||||||
take_some_while(is_ident_char).to_string()
|
take_some_while(is_ident_char)?.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[parser]
|
#[parser]
|
||||||
fn table<'a>(input: &mut &'a str) -> ParseResult<&'a str, Value> {
|
fn key_value<'a>(input: &mut &'a str) -> Result<(String, Value), &'a str> {
|
||||||
eat('{');
|
let key = (surrounded(key, is_whitespace)?, eat('=')?).0.to_string();
|
||||||
|
(key, surrounded(value, is_whitespace)?)
|
||||||
let mut values = BTreeMap::new();
|
|
||||||
try_repeat_while!(eat(','), {
|
|
||||||
let key = surrounded(key, is_whitespace);
|
|
||||||
(eat('='), skip_while(is_whitespace));
|
|
||||||
values.insert(key, value())
|
|
||||||
});
|
|
||||||
|
|
||||||
eat('}');
|
|
||||||
Value::Table(values)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[parser]
|
#[parser]
|
||||||
fn value<'a>(input: &mut &'a str) -> ParseResult<&'a str, Value> {
|
fn table<'a>(input: &mut &'a str) -> Result<Value, &'a str> {
|
||||||
skip_while(is_whitespace);
|
Value::Table(collection('{', key_value, ',', '}')?)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[parser]
|
||||||
|
fn value<'a>(input: &mut &'a str) -> Result<Value, &'a str> {
|
||||||
|
skip_while(is_whitespace)?;
|
||||||
let val = switch! {
|
let val = switch! {
|
||||||
eat_slice("true") => Value::Boolean(true),
|
eat_slice("true") => Value::Boolean(true),
|
||||||
eat_slice("false") => Value::Boolean(false),
|
eat_slice("false") => Value::Boolean(false),
|
||||||
peek('{') => table(),
|
peek('{') => table()?,
|
||||||
peek('[') => array(),
|
peek('[') => array()?,
|
||||||
peek('"') => Value::String(delimited('"', |_| true, '"').to_string()),
|
peek('"') => Value::String(delimited('"', |_| true, '"')?.to_string()),
|
||||||
_ => {
|
_ => {
|
||||||
let value_str = take_some_while(is_not_separator);
|
let value_str = take_some_while(is_not_separator)?;
|
||||||
// FIXME: Silence warning from pear.
|
|
||||||
if let Ok(int) = value_str.parse::<i64>() {
|
if let Ok(int) = value_str.parse::<i64>() {
|
||||||
Value::Integer(int)
|
Value::Integer(int)
|
||||||
} else if let Ok(float) = value_str.parse::<f64>() {
|
} else if let Ok(float) = value_str.parse::<f64>() {
|
||||||
|
@ -77,15 +71,12 @@ fn value<'a>(input: &mut &'a str) -> ParseResult<&'a str, Value> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
skip_while(is_whitespace);
|
skip_while(is_whitespace)?;
|
||||||
val
|
val
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_simple_toml_value(mut input: &str) -> Result<Value, String> {
|
pub fn parse_simple_toml_value(mut input: &str) -> StdResult<Value, String> {
|
||||||
let result: Result<Value, ParseError<&str>> =
|
parse!(value: &mut input).map_err(|e| e.to_string())
|
||||||
parse!(&mut input, (value(), eof()).0).into();
|
|
||||||
|
|
||||||
result.map_err(|e| e.to_string())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A simple wrapper over a `Value` reference with a custom implementation of
|
/// A simple wrapper over a `Value` reference with a custom implementation of
|
||||||
|
|
|
@ -85,6 +85,21 @@ pub enum AcceptParams {
|
||||||
Dynamic(SmallVec<[QMediaType; 1]>)
|
Dynamic(SmallVec<[QMediaType; 1]>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ::pear::parsers::Collection for AcceptParams {
|
||||||
|
type Item = QMediaType;
|
||||||
|
|
||||||
|
fn new() -> Self {
|
||||||
|
AcceptParams::Dynamic(SmallVec::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add(&mut self, item: Self::Item) {
|
||||||
|
match *self {
|
||||||
|
AcceptParams::Static(..) => panic!("can't add to static collection!"),
|
||||||
|
AcceptParams::Dynamic(ref mut v) => v.push(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PartialEq for AcceptParams {
|
impl PartialEq for AcceptParams {
|
||||||
fn eq(&self, other: &AcceptParams) -> bool {
|
fn eq(&self, other: &AcceptParams) -> bool {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -143,7 +158,7 @@ impl PartialEq for AcceptParams {
|
||||||
/// let response = Response::build().header(Accept::JSON).finalize();
|
/// let response = Response::build().header(Accept::JSON).finalize();
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Accept(AcceptParams);
|
pub struct Accept(pub(crate) AcceptParams);
|
||||||
|
|
||||||
macro_rules! accept_constructor {
|
macro_rules! accept_constructor {
|
||||||
($($name:ident ($check:ident): $str:expr, $t:expr,
|
($($name:ident ($check:ident): $str:expr, $t:expr,
|
||||||
|
|
|
@ -5,21 +5,36 @@ use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
use ext::IntoCollection;
|
use ext::IntoCollection;
|
||||||
use http::uncased::{uncased_eq, UncasedStr};
|
use http::uncased::{uncased_eq, UncasedStr};
|
||||||
use http::parse::{IndexedStr, parse_media_type};
|
use http::parse::{Indexed, IndexedString, parse_media_type};
|
||||||
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct MediaParam {
|
struct MediaParam {
|
||||||
key: IndexedStr,
|
key: IndexedString,
|
||||||
value: IndexedStr,
|
value: IndexedString,
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: `Static` is needed for `const` items. Need `const SmallVec::new`.
|
// FIXME: `Static` is needed for `const` items. Need `const SmallVec::new`.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum MediaParams {
|
pub enum MediaParams {
|
||||||
Static(&'static [(IndexedStr, IndexedStr)]),
|
Static(&'static [(IndexedString, IndexedString)]),
|
||||||
Dynamic(SmallVec<[(IndexedStr, IndexedStr); 2]>)
|
Dynamic(SmallVec<[(IndexedString, IndexedString); 2]>)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::pear::parsers::Collection for MediaParams {
|
||||||
|
type Item = (IndexedString, IndexedString);
|
||||||
|
|
||||||
|
fn new() -> Self {
|
||||||
|
MediaParams::Dynamic(SmallVec::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add(&mut self, item: Self::Item) {
|
||||||
|
match *self {
|
||||||
|
MediaParams::Static(..) => panic!("can't add to static collection!"),
|
||||||
|
MediaParams::Dynamic(ref mut v) => v.push(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
@ -90,17 +105,17 @@ pub struct MediaType {
|
||||||
pub source: Source,
|
pub source: Source,
|
||||||
/// The top-level type.
|
/// The top-level type.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub top: IndexedStr,
|
pub top: IndexedString,
|
||||||
/// The subtype.
|
/// The subtype.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub sub: IndexedStr,
|
pub sub: IndexedString,
|
||||||
/// The parameters, if any.
|
/// The parameters, if any.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub params: MediaParams
|
pub params: MediaParams
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! media_str {
|
macro_rules! media_str {
|
||||||
($string:expr) => (IndexedStr::Concrete(Cow::Borrowed($string)))
|
($string:expr) => (Indexed::Concrete(Cow::Borrowed($string)))
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! media_types {
|
macro_rules! media_types {
|
||||||
|
@ -276,8 +291,8 @@ impl MediaType {
|
||||||
{
|
{
|
||||||
MediaType {
|
MediaType {
|
||||||
source: Source::None,
|
source: Source::None,
|
||||||
top: IndexedStr::Concrete(top.into()),
|
top: Indexed::Concrete(top.into()),
|
||||||
sub: IndexedStr::Concrete(sub.into()),
|
sub: Indexed::Concrete(sub.into()),
|
||||||
params: MediaParams::Static(&[]),
|
params: MediaParams::Static(&[]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -313,14 +328,14 @@ impl MediaType {
|
||||||
P: IntoCollection<(K, V)>
|
P: IntoCollection<(K, V)>
|
||||||
{
|
{
|
||||||
let params = ps.mapped(|(key, val)| (
|
let params = ps.mapped(|(key, val)| (
|
||||||
IndexedStr::Concrete(key.into()),
|
Indexed::Concrete(key.into()),
|
||||||
IndexedStr::Concrete(val.into())
|
Indexed::Concrete(val.into())
|
||||||
));
|
));
|
||||||
|
|
||||||
MediaType {
|
MediaType {
|
||||||
source: Source::None,
|
source: Source::None,
|
||||||
top: IndexedStr::Concrete(top.into()),
|
top: Indexed::Concrete(top.into()),
|
||||||
sub: IndexedStr::Concrete(sub.into()),
|
sub: Indexed::Concrete(sub.into()),
|
||||||
params: MediaParams::Dynamic(params)
|
params: MediaParams::Dynamic(params)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -344,7 +359,7 @@ impl MediaType {
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn top(&self) -> &UncasedStr {
|
pub fn top(&self) -> &UncasedStr {
|
||||||
self.top.to_str(self.source.as_str()).into()
|
self.top.from_source(self.source.as_str()).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the subtype for this media type. The return type,
|
/// Returns the subtype for this media type. The return type,
|
||||||
|
@ -362,7 +377,7 @@ impl MediaType {
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn sub(&self) -> &UncasedStr {
|
pub fn sub(&self) -> &UncasedStr {
|
||||||
self.sub.to_str(self.source.as_str()).into()
|
self.sub.from_source(self.source.as_str()).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a `u8` representing how specific the top-level type and subtype
|
/// Returns a `u8` representing how specific the top-level type and subtype
|
||||||
|
@ -471,7 +486,7 @@ impl MediaType {
|
||||||
param_slice.iter()
|
param_slice.iter()
|
||||||
.map(move |&(ref key, ref val)| {
|
.map(move |&(ref key, ref val)| {
|
||||||
let source_str = self.source.as_str();
|
let source_str = self.source.as_str();
|
||||||
(key.to_str(source_str), val.to_str(source_str))
|
(key.from_source(source_str), val.from_source(source_str))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ pub(crate) mod parse;
|
||||||
// We need to export these for codegen, but otherwise it's unnecessary.
|
// We need to export these for codegen, but otherwise it's unnecessary.
|
||||||
// TODO: Expose a `const fn` from ContentType when possible. (see RFC#1817)
|
// TODO: Expose a `const fn` from ContentType when possible. (see RFC#1817)
|
||||||
pub mod uncased;
|
pub mod uncased;
|
||||||
#[doc(hidden)] pub use self::parse::IndexedStr;
|
#[doc(hidden)] pub use self::parse::Indexed;
|
||||||
#[doc(hidden)] pub use self::media_type::{MediaParams, Source};
|
#[doc(hidden)] pub use self::media_type::{MediaParams, Source};
|
||||||
|
|
||||||
pub use self::method::Method;
|
pub use self::method::Method;
|
||||||
|
|
|
@ -1,37 +1,34 @@
|
||||||
use pear::{ParseResult, ParseError};
|
use pear::parser;
|
||||||
use pear::parsers::*;
|
use pear::parsers::*;
|
||||||
|
|
||||||
use http::parse::checkers::is_whitespace;
|
use http::parse::checkers::is_whitespace;
|
||||||
use http::parse::media_type::media_type;
|
use http::parse::media_type::media_type;
|
||||||
use http::{MediaType, Accept, QMediaType};
|
use http::{Accept, QMediaType};
|
||||||
|
use http::parse::{Input, Result};
|
||||||
|
|
||||||
fn q<'a>(_: &'a str, media_type: &MediaType) -> ParseResult<&'a str, Option<f32>> {
|
#[parser]
|
||||||
match media_type.params().next() {
|
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", value)) if value.len() <= 5 => match value.parse::<f32>().ok() {
|
||||||
Some(q) if q > 1. => ParseError::custom("accept", "q value must be <= 1"),
|
Some(q) if q > 1. => return Err(pear_error!("q value must be <= 1")),
|
||||||
Some(q) if q < 0. => ParseError::custom("accept", "q value must be > 0"),
|
Some(q) if q < 0. => return Err(pear_error!("q value must be > 0")),
|
||||||
Some(q) => ParseResult::Done(Some(q)),
|
Some(q) => Some(q),
|
||||||
None => ParseError::custom("accept", "q value must be float")
|
None => return Err(pear_error!("invalid media-type weight"))
|
||||||
},
|
},
|
||||||
_ => ParseResult::Done(None)
|
_ => None
|
||||||
}
|
};
|
||||||
|
|
||||||
|
QMediaType(media_type, weight)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[parser]
|
#[parser]
|
||||||
fn accept<'a>(input: &mut &'a str) -> ParseResult<&'a str, Accept> {
|
fn accept<'a>(input: &mut Input<'a>) -> Result<'a, Accept> {
|
||||||
let mut media_types = vec![];
|
Accept(series(false, ',', is_whitespace, weighted_media_type)?)
|
||||||
repeat_while!(eat(','), {
|
|
||||||
skip_while(is_whitespace);
|
|
||||||
let media_type = media_type();
|
|
||||||
let weight = q(&media_type);
|
|
||||||
media_types.push(QMediaType(media_type, weight));
|
|
||||||
});
|
|
||||||
|
|
||||||
Accept::new(media_types)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_accept(mut input: &str) -> Result<Accept, ParseError<&str>> {
|
pub fn parse_accept(input: &str) -> Result<Accept> {
|
||||||
parse!(&mut input, (accept(), eof()).0).into()
|
parse!(accept: &mut input.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -0,0 +1,321 @@
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::ops::{Index, Range};
|
||||||
|
use std::fmt::{self, Debug};
|
||||||
|
|
||||||
|
use pear::{Input, Length};
|
||||||
|
|
||||||
|
pub type IndexedString = Indexed<'static, str>;
|
||||||
|
|
||||||
|
pub trait AsPtr {
|
||||||
|
fn as_ptr(&self) -> *const u8;
|
||||||
|
// unsafe fn from_raw<'a>(raw: *const u8, length: usize) -> &T;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsPtr for str {
|
||||||
|
#[inline(always)]
|
||||||
|
fn as_ptr(&self) -> *const u8 {
|
||||||
|
str::as_ptr(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsPtr for [u8] {
|
||||||
|
#[inline(always)]
|
||||||
|
fn as_ptr(&self) -> *const u8 {
|
||||||
|
<[u8]>::as_ptr(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub enum Indexed<'a, T: ?Sized + ToOwned + 'a> {
|
||||||
|
Indexed(usize, usize),
|
||||||
|
Concrete(Cow<'a, T>)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: ?Sized + ToOwned + 'a, C: Into<Cow<'a, T>>> From<C> for Indexed<'a, T> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn from(value: C) -> Indexed<'a, T> {
|
||||||
|
Indexed::Concrete(value.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: ?Sized + ToOwned + 'a> Indexed<'a, T> {
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn coerce<U: ?Sized + ToOwned>(self) -> Indexed<'a, U> {
|
||||||
|
match self {
|
||||||
|
Indexed::Indexed(a, b) => Indexed::Indexed(a, b),
|
||||||
|
_ => panic!("cannot convert indexed T to U unless indexed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: ?Sized + ToOwned + 'a> Indexed<'a, T> {
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn coerce_lifetime<'b>(self) -> Indexed<'b, T> {
|
||||||
|
match self {
|
||||||
|
Indexed::Indexed(a, b) => Indexed::Indexed(a, b),
|
||||||
|
_ => panic!("cannot coerce lifetime unless indexed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use std::ops::Add;
|
||||||
|
|
||||||
|
impl<'a, T: ?Sized + ToOwned + 'a> Add for Indexed<'a, T> {
|
||||||
|
type Output = Indexed<'a, T>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn add(self, other: Indexed<'a, T>) -> Indexed<'a, T> {
|
||||||
|
match self {
|
||||||
|
Indexed::Indexed(a, b) => match other {
|
||||||
|
Indexed::Indexed(c, d) if b == c && a < d => Indexed::Indexed(a, d),
|
||||||
|
_ => panic!("+ requires indexed")
|
||||||
|
}
|
||||||
|
_ => panic!("+ requires indexed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: ?Sized + ToOwned + 'a> Indexed<'a, T>
|
||||||
|
where T: Length + AsPtr + Index<Range<usize>, Output = T>
|
||||||
|
{
|
||||||
|
// Returns `None` if `needle` is not a substring of `haystack`.
|
||||||
|
pub fn checked_from(needle: &T, haystack: &T) -> Option<Indexed<'a, T>> {
|
||||||
|
let haystack_start = haystack.as_ptr() as usize;
|
||||||
|
let needle_start = needle.as_ptr() as usize;
|
||||||
|
|
||||||
|
if needle_start < haystack_start {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needle_start + needle.len()) > (haystack_start + haystack.len()) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let start = needle_start - haystack_start;
|
||||||
|
let end = start + needle.len();
|
||||||
|
Some(Indexed::Indexed(start, end))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Caller must ensure that `needle` is a substring of `haystack`.
|
||||||
|
pub unsafe fn unchecked_from(needle: &T, haystack: &T) -> Indexed<'a, T> {
|
||||||
|
let haystack_start = haystack.as_ptr() as usize;
|
||||||
|
let needle_start = needle.as_ptr() as usize;
|
||||||
|
|
||||||
|
let start = needle_start - haystack_start;
|
||||||
|
let end = start + needle.len();
|
||||||
|
Indexed::Indexed(start, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether this string is derived from indexes or not.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_indexed(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Indexed::Indexed(..) => true,
|
||||||
|
Indexed::Concrete(..) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether this string is derived from indexes or not.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.len() == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the string `self` corresponds to. If `self` is derived from
|
||||||
|
/// indexes, the corresponding subslice of `source` is returned. Otherwise,
|
||||||
|
/// the concrete string is returned.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if `self` is an indexed string and `string` is None.
|
||||||
|
// pub fn to_source(&self, source: Option<&'a T>) -> &T {
|
||||||
|
pub fn from_cow_source<'s>(&'s self, source: &'s Option<Cow<T>>) -> &'s T {
|
||||||
|
if self.is_indexed() && source.is_none() {
|
||||||
|
panic!("Cannot convert indexed str to str without base string!")
|
||||||
|
}
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
Indexed::Indexed(i, j) => &source.as_ref().unwrap()[i..j],
|
||||||
|
Indexed::Concrete(ref mstr) => mstr.as_ref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the string `self` corresponds to. If `self` is derived from
|
||||||
|
/// indexes, the corresponding subslice of `string` is returned. Otherwise,
|
||||||
|
/// the concrete string is returned.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if `self` is an indexed string and `string` is None.
|
||||||
|
pub fn from_source<'s>(&'s self, source: Option<&'s T>) -> &'s T {
|
||||||
|
if self.is_indexed() && source.is_none() {
|
||||||
|
panic!("Cannot convert indexed str to str without base string!")
|
||||||
|
}
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
Indexed::Indexed(i, j) => &source.unwrap()[(i as usize)..(j as usize)],
|
||||||
|
Indexed::Concrete(ref mstr) => &*mstr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: ToOwned + ?Sized + 'a> Clone for Indexed<'a, T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
match *self {
|
||||||
|
Indexed::Indexed(a, b) => Indexed::Indexed(a, b),
|
||||||
|
Indexed::Concrete(ref cow) => Indexed::Concrete(cow.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: ?Sized + 'a> Debug for Indexed<'a, T>
|
||||||
|
where T: ToOwned + Debug, T::Owned: Debug
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match *self {
|
||||||
|
Indexed::Indexed(a, b) => fmt::Debug::fmt(&(a, b), f),
|
||||||
|
Indexed::Concrete(ref cow) => fmt::Debug::fmt(cow, f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: ?Sized + Length + ToOwned + 'a> Length for Indexed<'a, T> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
match *self {
|
||||||
|
Indexed::Indexed(a, b) => (b - a) as usize,
|
||||||
|
Indexed::Concrete(ref cow) => cow.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct IndexedInput<'a, T: ?Sized + 'a> {
|
||||||
|
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<'a> IndexedInput<'a, [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 size = self.current.len() + n;
|
||||||
|
let addr = (current_addr - n) as *const u8;
|
||||||
|
self.current = unsafe { ::std::slice::from_raw_parts(addr, size) };
|
||||||
|
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);
|
|
@ -1,54 +0,0 @@
|
||||||
use std::borrow::Cow;
|
|
||||||
|
|
||||||
type Index = u32;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum IndexedStr {
|
|
||||||
Indexed(Index, Index),
|
|
||||||
Concrete(Cow<'static, str>)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IndexedStr {
|
|
||||||
/// Whether this string is derived from indexes or not.
|
|
||||||
pub fn is_indexed(&self) -> bool {
|
|
||||||
match *self {
|
|
||||||
IndexedStr::Indexed(..) => true,
|
|
||||||
IndexedStr::Concrete(..) => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieves the string `self` corresponds to. If `self` is derived from
|
|
||||||
/// indexes, the corresponding subslice of `string` is returned. Otherwise,
|
|
||||||
/// the concrete string is returned.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if `self` is an indexed string and `string` is None.
|
|
||||||
pub fn to_str<'a>(&'a self, string: Option<&'a str>) -> &'a str {
|
|
||||||
if self.is_indexed() && string.is_none() {
|
|
||||||
panic!("Cannot convert indexed str to str without base string!")
|
|
||||||
}
|
|
||||||
|
|
||||||
match *self {
|
|
||||||
IndexedStr::Indexed(i, j) => &string.unwrap()[(i as usize)..(j as usize)],
|
|
||||||
IndexedStr::Concrete(ref mstr) => &*mstr,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from(needle: &str, haystack: &str) -> Option<IndexedStr> {
|
|
||||||
let haystack_start = haystack.as_ptr() as usize;
|
|
||||||
let needle_start = needle.as_ptr() as usize;
|
|
||||||
|
|
||||||
if needle_start < haystack_start {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needle_start + needle.len()) > (haystack_start + haystack.len()) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let start = needle_start - haystack_start;
|
|
||||||
let end = start + needle.len();
|
|
||||||
Some(IndexedStr::Indexed(start as Index, end as Index))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,65 +1,59 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use pear::{ParseError, ParseResult};
|
use pear::{parser, switch};
|
||||||
use pear::parsers::*;
|
use pear::parsers::*;
|
||||||
use pear::combinators::*;
|
|
||||||
use smallvec::SmallVec;
|
|
||||||
|
|
||||||
use http::{MediaType, Source, MediaParams};
|
use http::{MediaType, Source};
|
||||||
use http::parse::checkers::{is_whitespace, is_valid_token};
|
use http::parse::checkers::{is_whitespace, is_valid_token};
|
||||||
use http::parse::IndexedStr;
|
use http::parse::{Input, Slice, Result};
|
||||||
|
|
||||||
#[parser]
|
#[parser]
|
||||||
fn quoted_string<'a>(input: &mut &'a str) -> ParseResult<&'a str, &'a str> {
|
fn quoted_string<'a>(input: &mut Input<'a>) -> Result<'a, Slice<'a>> {
|
||||||
eat('"');
|
eat('"')?;
|
||||||
|
|
||||||
let mut is_escaped = false;
|
let mut is_escaped = false;
|
||||||
let inner = take_while(|c| {
|
let inner = take_while(|c| {
|
||||||
if is_escaped { is_escaped = false; return true; }
|
if is_escaped { is_escaped = false; return true; }
|
||||||
if c == '\\' { is_escaped = true; return true; }
|
if c == '\\' { is_escaped = true; return true; }
|
||||||
c != '"'
|
c != '"'
|
||||||
});
|
})?;
|
||||||
|
|
||||||
eat('"');
|
eat('"')?;
|
||||||
inner
|
inner
|
||||||
}
|
}
|
||||||
|
|
||||||
#[parser]
|
#[parser]
|
||||||
pub fn media_type<'a>(input: &mut &'a str) -> ParseResult<&'a str, MediaType> {
|
fn media_param<'a>(input: &mut Input<'a>) -> Result<'a, (Slice<'a>, Slice<'a>)> {
|
||||||
let source: &str = *input;
|
let key = (take_some_while_until(is_valid_token, '=')?, eat('=')?).0;
|
||||||
|
let value = switch! {
|
||||||
|
peek('"') => quoted_string()?,
|
||||||
|
_ => take_some_while_until(is_valid_token, ';')?
|
||||||
|
};
|
||||||
|
|
||||||
let top = take_some_while(|c| is_valid_token(c) && c != '/');
|
(key, value)
|
||||||
eat('/');
|
}
|
||||||
let sub = take_some_while(is_valid_token);
|
|
||||||
|
|
||||||
let mut params = SmallVec::new();
|
#[parser]
|
||||||
switch_repeat! {
|
pub fn media_type<'a>(input: &mut Input<'a>) -> Result<'a, MediaType> {
|
||||||
surrounded(|i| eat(i, ';'), is_whitespace) => {
|
// FIXME: Explain the coerce safety.
|
||||||
let key = take_some_while(|c| is_valid_token(c) && c != '=');
|
let (top, sub, params) = unsafe {
|
||||||
eat('=');
|
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 value = switch! {
|
(top.coerce_lifetime(), sub.coerce_lifetime(), params)
|
||||||
peek('"') => quoted_string(),
|
};
|
||||||
_ => take_some_while(|c| is_valid_token(c) && c != ';')
|
|
||||||
};
|
|
||||||
|
|
||||||
let indexed_key = IndexedStr::from(key, source).expect("key");
|
|
||||||
let indexed_val = IndexedStr::from(value, source).expect("val");
|
|
||||||
params.push((indexed_key, indexed_val))
|
|
||||||
},
|
|
||||||
_ => break
|
|
||||||
}
|
|
||||||
|
|
||||||
MediaType {
|
MediaType {
|
||||||
source: Source::Custom(Cow::Owned(source.to_string())),
|
source: Source::Custom(Cow::Owned(input.source().to_string())),
|
||||||
top: IndexedStr::from(top, source).expect("top in source"),
|
top, sub, params
|
||||||
sub: IndexedStr::from(sub, source).expect("sub in source"),
|
|
||||||
params: MediaParams::Dynamic(params)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_media_type(mut input: &str) -> Result<MediaType, ParseError<&str>> {
|
pub fn parse_media_type(input: &str) -> Result<MediaType> {
|
||||||
parse!(&mut input, (media_type(), eof()).0).into()
|
parse!(media_type: &mut input.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
mod media_type;
|
mod media_type;
|
||||||
mod accept;
|
mod accept;
|
||||||
mod indexed_str;
|
mod indexed;
|
||||||
mod checkers;
|
mod checkers;
|
||||||
|
|
||||||
pub use self::indexed_str::*;
|
pub use self::indexed::*;
|
||||||
pub use self::media_type::*;
|
pub use self::media_type::*;
|
||||||
pub use self::accept::*;
|
pub use self::accept::*;
|
||||||
|
|
||||||
|
pub type Input<'a> = IndexedInput<'a, str>;
|
||||||
|
pub type Slice<'a> = Indexed<'a, str>;
|
||||||
|
pub type Result<'a, T> = ::pear::Result<T, Input<'a>>;
|
||||||
|
|
|
@ -4,9 +4,10 @@
|
||||||
#![feature(try_trait)]
|
#![feature(try_trait)]
|
||||||
#![feature(fnbox)]
|
#![feature(fnbox)]
|
||||||
#![feature(never_type)]
|
#![feature(never_type)]
|
||||||
#![recursion_limit="256"]
|
#![feature(proc_macro)]
|
||||||
|
#![feature(proc_macro_non_items)]
|
||||||
|
|
||||||
#![plugin(pear_codegen)]
|
#![recursion_limit="256"]
|
||||||
|
|
||||||
// TODO: Version URLs.
|
// TODO: Version URLs.
|
||||||
#![doc(html_root_url = "https://api.rocket.rs")]
|
#![doc(html_root_url = "https://api.rocket.rs")]
|
||||||
|
|
Loading…
Reference in New Issue