mirror of https://github.com/rwf2/Rocket.git
Implement the (currently unused) MediaType struct.
This commit is contained in:
parent
7808ad1649
commit
d09b4138d9
|
@ -28,6 +28,9 @@ state = "0.2"
|
|||
time = "0.1"
|
||||
memchr = "1"
|
||||
base64 = "0.4"
|
||||
smallvec = "0.3"
|
||||
pear = "0.0.2"
|
||||
pear_codegen = "0.0.2"
|
||||
|
||||
[dependencies.cookie]
|
||||
version = "0.7.2"
|
||||
|
|
|
@ -498,6 +498,7 @@ mod test {
|
|||
|
||||
const TEST_CONFIG_FILENAME: &'static str = "/tmp/testing/Rocket.toml";
|
||||
|
||||
// TODO: It's a shame we have to depend on lazy_static just for this.
|
||||
lazy_static! {
|
||||
static ref ENV_LOCK: Mutex<usize> = Mutex::new(0);
|
||||
}
|
||||
|
|
|
@ -90,6 +90,13 @@ impl Ord for UncasedAsciiRef {
|
|||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for UncasedAsciiRef {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// An uncased (case-preserving) ASCII string.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct UncasedAscii<'s> {
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
macro_rules! known_media_types {
|
||||
($cont:ident) => ($cont! {
|
||||
Any (is_any): "any Content-Type", "*", "*",
|
||||
HTML (is_html): "HTML", "text", "html" ; "charset" => "utf-8",
|
||||
Plain (is_plain): "plaintext", "text", "plain" ; "charset" => "utf-8",
|
||||
JSON (is_json): "JSON", "application", "json",
|
||||
Form (is_form): "forms", "application", "x-www-form-urlencoded",
|
||||
JavaScript (is_javascript): "JavaScript", "application", "javascript",
|
||||
CSS (is_css): "CSS", "text", "css" ; "charset" => "utf-8",
|
||||
FormData (is_form_data): "multipart form data", "multipart", "form-data",
|
||||
XML (is_xml): "XML", "text", "xml" ; "charset" => "utf-8",
|
||||
CSV (is_csv): "CSV", "text", "csv" ; "charset" => "utf-8",
|
||||
PNG (is_png): "PNG", "image", "png",
|
||||
GIF (is_gif): "GIF", "image", "gif",
|
||||
BMP (is_bmp): "BMP", "image", "bmp",
|
||||
JPEG (is_jpeg): "JPEG", "image", "jpeg",
|
||||
WEBP (is_webp): "WEBP", "image", "webp",
|
||||
SVG (is_svg): "SVG", "image", "svg+xml",
|
||||
PDF (is_pdf): "PDF", "application", "pdf",
|
||||
TTF (is_ttf): "TTF", "application", "font-sfnt",
|
||||
OTF (is_otf): "OTF", "application", "font-sfnt",
|
||||
WOFF (is_woff): "WOFF", "application", "font-woff",
|
||||
WOFF2 (is_woff2): "WOFF2", "font", "woff2"
|
||||
})
|
||||
}
|
||||
|
||||
macro_rules! known_extensions {
|
||||
($cont:ident) => ($cont! {
|
||||
"txt" => Plain,
|
||||
"html" => HTML,
|
||||
"htm" => HTML,
|
||||
"xml" => XML,
|
||||
"csv" => CSV,
|
||||
"js" => JavaScript,
|
||||
"css" => CSS,
|
||||
"json" => JSON,
|
||||
"png" => PNG,
|
||||
"gif" => GIF,
|
||||
"bmp" => BMP,
|
||||
"jpeg" => JPEG,
|
||||
"jpg" => JPEG,
|
||||
"webp" => WEBP,
|
||||
"svg" => SVG,
|
||||
"pdf" => PDF,
|
||||
"ttf" => TTF,
|
||||
"otf" => OTF,
|
||||
"woff" => WOFF,
|
||||
"woff2" => WOFF2
|
||||
})
|
||||
}
|
|
@ -0,0 +1,375 @@
|
|||
use std::borrow::Cow;
|
||||
use std::str::FromStr;
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use http::ascii::{uncased_eq, UncasedAsciiRef};
|
||||
use http::parse::{IndexedStr, parse_media_type};
|
||||
|
||||
use smallvec::SmallVec;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct MediaParam {
|
||||
key: IndexedStr,
|
||||
value: IndexedStr,
|
||||
}
|
||||
|
||||
// FIXME: `Static` is needed for `const` items. Need `const SmallVec::new`.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum MediaParams {
|
||||
Empty,
|
||||
Static(&'static [(IndexedStr, IndexedStr)]),
|
||||
Dynamic(SmallVec<[(IndexedStr, IndexedStr); 2]>)
|
||||
}
|
||||
|
||||
// TODO: impl PartialEq, Hash for `MediaType`.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MediaType {
|
||||
/// Storage for the entire media type string. This will be `Some` when the
|
||||
/// media type was parsed from a string and `None` when it was created
|
||||
/// manually.
|
||||
#[doc(hidden)]
|
||||
pub source: Option<Cow<'static, str>>,
|
||||
/// The top-level type.
|
||||
#[doc(hidden)]
|
||||
pub top: IndexedStr,
|
||||
/// The subtype.
|
||||
#[doc(hidden)]
|
||||
pub sub: IndexedStr,
|
||||
/// The parameters, if any.
|
||||
#[doc(hidden)]
|
||||
pub params: MediaParams
|
||||
}
|
||||
|
||||
macro_rules! media_str {
|
||||
($string:expr) => (IndexedStr::Concrete(Cow::Borrowed($string)))
|
||||
}
|
||||
|
||||
macro_rules! media_types {
|
||||
($($name:ident ($check:ident): $str:expr, $t:expr,
|
||||
$s:expr $(; $k:expr => $v:expr)*),+) => {
|
||||
$(
|
||||
#[doc="[MediaType](struct.MediaType.html) for <b>"]
|
||||
#[doc=$str]
|
||||
#[doc="</b>: <i>"] #[doc=$t] #[doc="/"] #[doc=$s] #[doc="</i>"]
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const $name: MediaType = MediaType {
|
||||
source: None,
|
||||
top: media_str!($t),
|
||||
sub: media_str!($s),
|
||||
params: MediaParams::Static(&[$((media_str!($k), media_str!($v))),*])
|
||||
};
|
||||
|
||||
#[inline(always)]
|
||||
pub fn $check(&self) -> bool {
|
||||
*self == MediaType::$name
|
||||
}
|
||||
)+
|
||||
|
||||
/// Returns `true` if this MediaType is known to Rocket, that is,
|
||||
/// there is an associated constant for `self`.
|
||||
pub fn is_known(&self) -> bool {
|
||||
$(if self.$check() { return true })+
|
||||
false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! from_extension {
|
||||
($($ext:expr => $name:ident),*) => (
|
||||
pub fn from_extension(ext: &str) -> Option<MediaType> {
|
||||
match ext {
|
||||
$(x if uncased_eq(x, $ext) => Some(MediaType::$name)),*,
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
impl MediaType {
|
||||
#[inline]
|
||||
pub fn new<T, S>(top: T, sub: S) -> MediaType
|
||||
where T: Into<Cow<'static, str>>, S: Into<Cow<'static, str>>
|
||||
{
|
||||
MediaType {
|
||||
source: None,
|
||||
top: IndexedStr::Concrete(top.into()),
|
||||
sub: IndexedStr::Concrete(sub.into()),
|
||||
params: MediaParams::Empty,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn with_params<T, S, K, V, P>(top: T, sub: S, ps: P) -> MediaType
|
||||
where T: Into<Cow<'static, str>>, S: Into<Cow<'static, str>>,
|
||||
K: Into<Cow<'static, str>>, V: Into<Cow<'static, str>>,
|
||||
P: IntoIterator<Item=(K, V)>
|
||||
{
|
||||
let mut params = SmallVec::new();
|
||||
for (key, val) in ps {
|
||||
params.push((
|
||||
IndexedStr::Concrete(key.into()),
|
||||
IndexedStr::Concrete(val.into())
|
||||
))
|
||||
}
|
||||
|
||||
MediaType {
|
||||
source: None,
|
||||
top: IndexedStr::Concrete(top.into()),
|
||||
sub: IndexedStr::Concrete(sub.into()),
|
||||
params: MediaParams::Dynamic(params)
|
||||
}
|
||||
}
|
||||
|
||||
known_extensions!(from_extension);
|
||||
|
||||
#[inline]
|
||||
pub fn top(&self) -> &UncasedAsciiRef {
|
||||
self.top.to_str(self.source.as_ref()).into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn sub(&self) -> &UncasedAsciiRef {
|
||||
self.sub.to_str(self.source.as_ref()).into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn params<'a>(&'a self) -> impl Iterator<Item=(&'a str, &'a str)> + 'a {
|
||||
let param_slice = match self.params {
|
||||
MediaParams::Static(slice) => slice,
|
||||
MediaParams::Dynamic(ref vec) => &vec[..],
|
||||
MediaParams::Empty => &[]
|
||||
};
|
||||
|
||||
param_slice.iter()
|
||||
.map(move |&(ref key, ref val)| {
|
||||
let source_str = self.source.as_ref();
|
||||
(key.to_str(source_str), val.to_str(source_str))
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn into_owned(self) -> MediaType {
|
||||
MediaType {
|
||||
source: self.source.map(|c| c.into_owned().into()),
|
||||
top: self.top,
|
||||
sub: self.sub,
|
||||
params: self.params
|
||||
}
|
||||
}
|
||||
|
||||
known_media_types!(media_types);
|
||||
}
|
||||
|
||||
impl FromStr for MediaType {
|
||||
// Ideally we'd return a `ParseError`, but that required a lifetime.
|
||||
type Err = String;
|
||||
|
||||
#[inline]
|
||||
fn from_str(raw: &str) -> Result<MediaType, String> {
|
||||
parse_media_type(raw)
|
||||
.map(|mt| mt.into_owned())
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for MediaType {
|
||||
fn eq(&self, other: &MediaType) -> bool {
|
||||
self.top() == other.top() && self.sub() == other.sub()
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for MediaType {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.top().hash(state);
|
||||
self.sub().hash(state);
|
||||
|
||||
for (key, val) in self.params() {
|
||||
key.hash(state);
|
||||
val.hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for MediaType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}/{}", self.top(), self.sub())?;
|
||||
for (key, val) in self.params() {
|
||||
write!(f, "; {}={}", key, val)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::str::FromStr;
|
||||
use super::MediaType;
|
||||
|
||||
macro_rules! assert_no_parse {
|
||||
($string:expr) => ({
|
||||
let result = MediaType::from_str($string);
|
||||
if result.is_ok() {
|
||||
panic!("{:?} parsed unexpectedly.", $string)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
macro_rules! assert_parse {
|
||||
($string:expr) => ({
|
||||
let result = MediaType::from_str($string);
|
||||
match result {
|
||||
Ok(media_type) => media_type,
|
||||
Err(e) => panic!("{:?} failed to parse: {}", $string, e)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
macro_rules! assert_parse_eq {
|
||||
(@full $string:expr, $result:expr, $(($k:expr, $v:expr)),*) => ({
|
||||
let result = assert_parse!($string);
|
||||
assert_eq!(result, $result);
|
||||
|
||||
let result = assert_parse!($string);
|
||||
assert_eq!(result, $result);
|
||||
|
||||
let expected_params: Vec<(&str, &str)> = vec![$(($k, $v)),*];
|
||||
if expected_params.len() > 0 {
|
||||
assert_eq!(result.params().count(), expected_params.len());
|
||||
let all_params = result.params().zip(expected_params.iter());
|
||||
for ((key, val), &(ekey, eval)) in all_params {
|
||||
assert_eq!(key, ekey);
|
||||
assert_eq!(val, eval);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
(from: $string:expr, into: $result:expr)
|
||||
=> (assert_parse_eq!(@full $string, $result, ));
|
||||
(from: $string:expr, into: $result:expr, params: $(($key:expr, $val:expr)),*)
|
||||
=> (assert_parse_eq!(@full $string, $result, $(($key, $val)),*));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_does_parse() {
|
||||
assert_parse!("text/html");
|
||||
assert_parse!("a/b");
|
||||
assert_parse!("*/*");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_parse_eq() {
|
||||
assert_parse_eq!(from: "text/html", into: MediaType::HTML);
|
||||
assert_parse_eq!(from: "text/html; charset=utf-8", into: MediaType::HTML);
|
||||
assert_parse_eq!(from: "text/html", into: MediaType::new("text", "html"));
|
||||
|
||||
assert_parse_eq!(from: "a/b", into: MediaType::new("a", "b"));
|
||||
assert_parse_eq!(from: "*/*", into: MediaType::Any);
|
||||
assert_parse_eq!(from: "application/pdf", into: MediaType::PDF);
|
||||
assert_parse_eq!(from: "application/json", into: MediaType::JSON);
|
||||
assert_parse_eq!(from: "image/svg+xml", into: MediaType::SVG);
|
||||
|
||||
assert_parse_eq!(from: "*/json", into: MediaType::new("*", "json"));
|
||||
assert_parse_eq! {
|
||||
from: "application/*; param=1",
|
||||
into: MediaType::new("application", "*")
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_param_eq() {
|
||||
assert_parse_eq! {
|
||||
from: "text/html; a=b; b=c; c=d",
|
||||
into: MediaType::new("text", "html"),
|
||||
params: ("a", "b"), ("b", "c"), ("c", "d")
|
||||
};
|
||||
|
||||
assert_parse_eq! {
|
||||
from: "text/html;a=b;b=c; c=d; d=e",
|
||||
into: MediaType::new("text", "html"),
|
||||
params: ("a", "b"), ("b", "c"), ("c", "d"), ("d", "e")
|
||||
};
|
||||
|
||||
assert_parse_eq! {
|
||||
from: "text/html; charset=utf-8",
|
||||
into: MediaType::new("text", "html"),
|
||||
params: ("charset", "utf-8")
|
||||
};
|
||||
|
||||
assert_parse_eq! {
|
||||
from: "application/*; param=1",
|
||||
into: MediaType::new("application", "*"),
|
||||
params: ("param", "1")
|
||||
};
|
||||
|
||||
assert_parse_eq! {
|
||||
from: "*/*;q=0.5;b=c;c=d",
|
||||
into: MediaType::Any,
|
||||
params: ("q", "0.5"), ("b", "c"), ("c", "d")
|
||||
};
|
||||
|
||||
assert_parse_eq! {
|
||||
from: "multipart/form-data; boundary=----WebKitFormBoundarypRshfItmvaC3aEuq",
|
||||
into: MediaType::FormData,
|
||||
params: ("boundary", "----WebKitFormBoundarypRshfItmvaC3aEuq")
|
||||
};
|
||||
|
||||
assert_parse_eq! {
|
||||
from: r#"*/*; a="hello, world!@#$%^&*();;hi""#,
|
||||
into: MediaType::Any,
|
||||
params: ("a", "hello, world!@#$%^&*();;hi")
|
||||
};
|
||||
|
||||
assert_parse_eq! {
|
||||
from: r#"application/json; a=";,;""#,
|
||||
into: MediaType::JSON,
|
||||
params: ("a", ";,;")
|
||||
};
|
||||
|
||||
assert_parse_eq! {
|
||||
from: r#"application/json; a=";,;"; b=c"#,
|
||||
into: MediaType::JSON,
|
||||
params: ("a", ";,;"), ("b", "c")
|
||||
};
|
||||
|
||||
assert_parse_eq! {
|
||||
from: r#"application/json; b=c; a=";.,.;""#,
|
||||
into: MediaType::JSON,
|
||||
params: ("b", "c"), ("a", ";.,.;")
|
||||
};
|
||||
|
||||
assert_parse_eq! {
|
||||
from: r#"*/*; a="a"; b="b"; a=a; b=b; c=c"#,
|
||||
into: MediaType::Any,
|
||||
params: ("a", "a"), ("b", "b"), ("a", "a"), ("b", "b"), ("c", "c")
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_params_do_parse() {
|
||||
assert_parse!("*/*; q=1; q=2");
|
||||
assert_parse!("*/*; q=1;q=2;q=3;a=v;c=1;da=1;sdlkldsadasd=uhisdcb89");
|
||||
assert_parse!("*/*; q=1; q=2");
|
||||
assert_parse!("*/*; q=1; q=2; a=b;c=d; e=f; a=s;a=e");
|
||||
assert_parse!("*/*; q=1; q=2 ; a=b");
|
||||
assert_parse!("*/*; q=1; q=2; hello=\"world !\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bad_parses() {
|
||||
assert_no_parse!("application//json");
|
||||
assert_no_parse!("application///json");
|
||||
assert_no_parse!("a/b;");
|
||||
assert_no_parse!("*/*; a=b;;");
|
||||
assert_no_parse!("*/*; a=b;a");
|
||||
assert_no_parse!("*/*; a=b; ");
|
||||
assert_no_parse!("*/*; a=b;");
|
||||
assert_no_parse!("*/*; a = b");
|
||||
assert_no_parse!("*/*; a= b");
|
||||
assert_no_parse!("*/*; a =b");
|
||||
assert_no_parse!(r#"*/*; a="b"#);
|
||||
assert_no_parse!(r#"*/*; a="b; c=d"#);
|
||||
assert_no_parse!(r#"*/*; a="b; c=d"#);
|
||||
}
|
||||
}
|
|
@ -8,12 +8,16 @@
|
|||
pub mod hyper;
|
||||
pub mod uri;
|
||||
|
||||
#[macro_use]
|
||||
mod known_media_types;
|
||||
mod cookies;
|
||||
mod session;
|
||||
mod method;
|
||||
mod media_type;
|
||||
mod content_type;
|
||||
mod status;
|
||||
mod header;
|
||||
mod parse;
|
||||
|
||||
// We need to export this for codegen, but otherwise it's unnecessary.
|
||||
// TODO: Expose a `const fn` from ContentType when possible. (see RFC#1817)
|
||||
|
@ -24,5 +28,6 @@ pub use self::content_type::ContentType;
|
|||
pub use self::status::{Status, StatusClass};
|
||||
pub use self::header::{Header, HeaderMap};
|
||||
|
||||
pub use self::media_type::*;
|
||||
pub use self::cookies::*;
|
||||
pub use self::session::*;
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
#[inline(always)]
|
||||
pub fn is_whitespace(byte: char) -> bool {
|
||||
byte == ' ' || byte == '\t'
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_valid_token(c: char) -> bool {
|
||||
match c {
|
||||
'0'...'9' | 'A'...'Z' | '^'...'~' | '#'...'\''
|
||||
| '!' | '*' | '+' | '-' | '.' => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
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 Cow<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))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use pear::ParseResult;
|
||||
use pear::parsers::*;
|
||||
use pear::combinators::*;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use http::{MediaType, MediaParams};
|
||||
use http::parse::checkers::{is_whitespace, is_valid_token};
|
||||
use http::parse::IndexedStr;
|
||||
|
||||
#[parser]
|
||||
fn quoted_string<'a>(input: &mut &'a str) -> ParseResult<&'a str, &'a str> {
|
||||
eat('"');
|
||||
|
||||
let mut is_escaped = false;
|
||||
let inner = take_while(|c| {
|
||||
if is_escaped { is_escaped = false; return true; }
|
||||
if c == '\\' { is_escaped = true; return true; }
|
||||
c != '"'
|
||||
});
|
||||
|
||||
eat('"');
|
||||
inner
|
||||
}
|
||||
|
||||
macro_rules! switch_repeat {
|
||||
($input:expr, $($cases:tt)*) => (repeat!($input, switch!($($cases)*)))
|
||||
}
|
||||
|
||||
#[parser]
|
||||
fn media_type<'a>(input: &mut &'a str,
|
||||
source: &'a str) -> ParseResult<&'a str, MediaType> {
|
||||
let top = take_some_while(|c| is_valid_token(c) && c != '/');
|
||||
eat('/');
|
||||
let sub = take_some_while(is_valid_token);
|
||||
|
||||
let mut params = SmallVec::new();
|
||||
switch_repeat! {
|
||||
surrounded(|i| eat(i, ';'), is_whitespace) => {
|
||||
skip_while(is_whitespace);
|
||||
let key = take_some_while(|c| is_valid_token(c) && c != '=');
|
||||
eat('=');
|
||||
|
||||
let value = switch! {
|
||||
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 {
|
||||
source: Some(Cow::Owned(source.to_string())),
|
||||
top: IndexedStr::from(top, source).expect("top in source"),
|
||||
sub: IndexedStr::from(sub, source).expect("sub in source"),
|
||||
params: MediaParams::Dynamic(params)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_media_type(mut input: &str) -> ParseResult<&str, MediaType> {
|
||||
parse!(&mut input, (media_type(input), eof()).0)
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
mod media_type;
|
||||
mod indexed_str;
|
||||
mod checkers;
|
||||
|
||||
pub use self::indexed_str::*;
|
||||
pub use self::media_type::*;
|
|
@ -6,6 +6,9 @@
|
|||
#![feature(type_ascription)]
|
||||
#![feature(pub_restricted)]
|
||||
#![feature(lookup_host)]
|
||||
#![feature(plugin)]
|
||||
|
||||
#![plugin(pear_codegen)]
|
||||
|
||||
//! # Rocket - Core API Documentation
|
||||
//!
|
||||
|
@ -95,6 +98,7 @@
|
|||
//!
|
||||
|
||||
#[macro_use] extern crate log;
|
||||
#[macro_use] extern crate pear;
|
||||
extern crate term_painter;
|
||||
extern crate hyper;
|
||||
extern crate url;
|
||||
|
@ -105,6 +109,7 @@ extern crate cookie;
|
|||
extern crate time;
|
||||
extern crate memchr;
|
||||
extern crate base64;
|
||||
extern crate smallvec;
|
||||
|
||||
#[cfg(test)] #[macro_use] extern crate lazy_static;
|
||||
|
||||
|
|
Loading…
Reference in New Issue