mirror of https://github.com/rwf2/Rocket.git
Allow shorthand for route format specifiers.
This commit is contained in:
parent
29d56900c6
commit
362f0ccdac
|
@ -23,6 +23,7 @@ fn method_to_path(ecx: &ExtCtxt, method: Method) -> Path {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: I think we also want the attributes here.
|
||||||
fn media_type_to_expr(ecx: &ExtCtxt, ct: Option<MediaType>) -> Option<P<Expr>> {
|
fn media_type_to_expr(ecx: &ExtCtxt, ct: Option<MediaType>) -> Option<P<Expr>> {
|
||||||
ct.map(|ct| {
|
ct.map(|ct| {
|
||||||
let (top, sub) = (ct.top().as_str(), ct.sub().as_str());
|
let (top, sub) = (ct.top().as_str(), ct.sub().as_str());
|
||||||
|
|
|
@ -258,7 +258,7 @@ fn parse_rank(ecx: &ExtCtxt, kv: &KVSpanned<LitKind>) -> isize {
|
||||||
|
|
||||||
fn parse_format(ecx: &ExtCtxt, kv: &KVSpanned<LitKind>) -> MediaType {
|
fn parse_format(ecx: &ExtCtxt, kv: &KVSpanned<LitKind>) -> MediaType {
|
||||||
if let LitKind::Str(ref s, _) = *kv.value() {
|
if let LitKind::Str(ref s, _) = *kv.value() {
|
||||||
if let Ok(ct) = MediaType::from_str(&s.as_str()) {
|
if let Some(ct) = MediaType::parse_flexible(&s.as_str()) {
|
||||||
if !ct.is_known() {
|
if !ct.is_known() {
|
||||||
let msg = format!("'{}' is not a known media type", s);
|
let msg = format!("'{}' is not a known media type", s);
|
||||||
ecx.span_warn(kv.value.span, &msg);
|
ecx.span_warn(kv.value.span, &msg);
|
||||||
|
@ -273,7 +273,8 @@ fn parse_format(ecx: &ExtCtxt, kv: &KVSpanned<LitKind>) -> MediaType {
|
||||||
ecx.struct_span_err(kv.span, r#"`format` must be a "media/type""#)
|
ecx.struct_span_err(kv.span, r#"`format` must be a "media/type""#)
|
||||||
.help(r#"format, if specified, must be a key-value pair where
|
.help(r#"format, if specified, must be a key-value pair where
|
||||||
the key is `format` and the value is a string representing the
|
the key is `format` and the value is a string representing the
|
||||||
media type accepted. e.g: format = "application/json""#)
|
media type accepted. e.g: format = "application/json".
|
||||||
|
shorthand is also accepted: format = "json"#)
|
||||||
.emit();
|
.emit();
|
||||||
|
|
||||||
MediaType::Any
|
MediaType::Any
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// must-compile-successfully
|
||||||
|
|
||||||
#![feature(plugin, decl_macro)]
|
#![feature(plugin, decl_macro)]
|
||||||
#![plugin(rocket_codegen)]
|
#![plugin(rocket_codegen)]
|
||||||
|
|
||||||
|
@ -12,7 +14,4 @@ fn two() -> &'static str { "hi" }
|
||||||
#[get("/", format = "x-custom/x-custom")] //~ WARNING not a known media type
|
#[get("/", format = "x-custom/x-custom")] //~ WARNING not a known media type
|
||||||
fn three() -> &'static str { "hi" }
|
fn three() -> &'static str { "hi" }
|
||||||
|
|
||||||
// Make the test fail here so we can actually check for the warnings above.
|
|
||||||
assert!(false);
|
|
||||||
|
|
||||||
fn main() { }
|
fn main() { }
|
||||||
|
|
|
@ -30,4 +30,4 @@ error[E0277]: the trait bound `S: rocket::http::uri::FromUriParam<_>` is not sat
|
||||||
|
|
||||||
error: aborting due to 4 previous errors
|
error: aborting due to 4 previous errors
|
||||||
|
|
||||||
If you want more information on this error, try using "rustc --explain E0277"
|
For more information about this error, try `rustc --explain E0277`.
|
||||||
|
|
|
@ -19,8 +19,9 @@ struct Person {
|
||||||
|
|
||||||
// In a `GET` request and all other non-payload supporting request types, the
|
// In a `GET` request and all other non-payload supporting request types, the
|
||||||
// preferred media type in the Accept header is matched against the `format` in
|
// preferred media type in the Accept header is matched against the `format` in
|
||||||
// the route attribute.
|
// the route attribute. Note: if this was a real application, we'd use
|
||||||
#[get("/<name>/<age>", format = "application/json")]
|
// `rocket_contrib`'s built-in JSON support and return a `JsonValue` instead.
|
||||||
|
#[get("/<name>/<age>", format = "json")]
|
||||||
fn get_hello(name: String, age: u8) -> content::Json<String> {
|
fn get_hello(name: String, age: u8) -> content::Json<String> {
|
||||||
// In a real application, we'd use the JSON contrib type.
|
// In a real application, we'd use the JSON contrib type.
|
||||||
let person = Person { name: name, age: age, };
|
let person = Person { name: name, age: age, };
|
||||||
|
@ -29,7 +30,7 @@ fn get_hello(name: String, age: u8) -> content::Json<String> {
|
||||||
|
|
||||||
// In a `POST` request and all other payload supporting request types, the
|
// In a `POST` request and all other payload supporting request types, the
|
||||||
// content type is matched against the `format` in the route attribute.
|
// content type is matched against the `format` in the route attribute.
|
||||||
#[post("/<age>", format = "text/plain", data = "<name>")]
|
#[post("/<age>", format = "plain", data = "<name>")]
|
||||||
fn post_hello(age: u8, name: String) -> content::Json<String> {
|
fn post_hello(age: u8, name: String) -> content::Json<String> {
|
||||||
let person = Person { name: name, age: age, };
|
let person = Person { name: name, age: age, };
|
||||||
content::Json(serde_json::to_string(&person).unwrap())
|
content::Json(serde_json::to_string(&person).unwrap())
|
||||||
|
|
|
@ -25,7 +25,7 @@ struct Message {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This example can be improved by using `route` with multiple HTTP verbs.
|
// TODO: This example can be improved by using `route` with multiple HTTP verbs.
|
||||||
#[post("/<id>", format = "application/json", data = "<message>")]
|
#[post("/<id>", format = "json", data = "<message>")]
|
||||||
fn new(id: ID, message: Json<Message>, map: State<MessageMap>) -> JsonValue {
|
fn new(id: ID, message: Json<Message>, map: State<MessageMap>) -> JsonValue {
|
||||||
let mut hashmap = map.lock().expect("map lock.");
|
let mut hashmap = map.lock().expect("map lock.");
|
||||||
if hashmap.contains_key(&id) {
|
if hashmap.contains_key(&id) {
|
||||||
|
@ -39,7 +39,7 @@ fn new(id: ID, message: Json<Message>, map: State<MessageMap>) -> JsonValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[put("/<id>", format = "application/json", data = "<message>")]
|
#[put("/<id>", format = "json", data = "<message>")]
|
||||||
fn update(id: ID, message: Json<Message>, map: State<MessageMap>) -> Option<JsonValue> {
|
fn update(id: ID, message: Json<Message>, map: State<MessageMap>) -> Option<JsonValue> {
|
||||||
let mut hashmap = map.lock().unwrap();
|
let mut hashmap = map.lock().unwrap();
|
||||||
if hashmap.contains_key(&id) {
|
if hashmap.contains_key(&id) {
|
||||||
|
@ -50,7 +50,7 @@ fn update(id: ID, message: Json<Message>, map: State<MessageMap>) -> Option<Json
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/<id>", format = "application/json")]
|
#[get("/<id>", format = "json")]
|
||||||
fn get(id: ID, map: State<MessageMap>) -> Option<Json<Message>> {
|
fn get(id: ID, map: State<MessageMap>) -> Option<Json<Message>> {
|
||||||
let hashmap = map.lock().unwrap();
|
let hashmap = map.lock().unwrap();
|
||||||
hashmap.get(&id).map(|contents| {
|
hashmap.get(&id).map(|contents| {
|
||||||
|
|
|
@ -15,7 +15,7 @@ struct Message {
|
||||||
contents: String
|
contents: String
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/<id>", format = "application/msgpack")]
|
#[get("/<id>", format = "msgpack")]
|
||||||
fn get(id: usize) -> MsgPack<Message> {
|
fn get(id: usize) -> MsgPack<Message> {
|
||||||
MsgPack(Message {
|
MsgPack(Message {
|
||||||
id: id,
|
id: id,
|
||||||
|
@ -23,7 +23,7 @@ fn get(id: usize) -> MsgPack<Message> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/", data = "<data>", format = "application/msgpack")]
|
#[post("/", data = "<data>", format = "msgpack")]
|
||||||
fn create(data: MsgPack<Message>) -> Result<String, ()> {
|
fn create(data: MsgPack<Message>) -> Result<String, ()> {
|
||||||
Ok(data.into_inner().contents)
|
Ok(data.into_inner().contents)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ extern crate rocket;
|
||||||
use std::io;
|
use std::io;
|
||||||
use rocket::Data;
|
use rocket::Data;
|
||||||
|
|
||||||
#[post("/upload", format = "text/plain", data = "<data>")]
|
#[post("/upload", format = "plain", data = "<data>")]
|
||||||
fn upload(data: Data) -> io::Result<String> {
|
fn upload(data: Data) -> io::Result<String> {
|
||||||
data.stream_to_file("/tmp/upload.txt").map(|n| n.to_string())
|
data.stream_to_file("/tmp/upload.txt").map(|n| n.to_string())
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
macro_rules! docify {
|
||||||
|
([$($doc:tt)*]; $($tt:tt)*) => {
|
||||||
|
docify!([$($doc)*] [] $($tt)*);
|
||||||
|
};
|
||||||
|
|
||||||
|
// FIXME: Treat $a just like everywhere else. What if we start with @[]?
|
||||||
|
([$a:tt $($b:tt)*] [] $($tt:tt)*) => {
|
||||||
|
docify!([$($b)*] [stringify!($a), " "] $($tt)*);
|
||||||
|
};
|
||||||
|
|
||||||
|
([@fence @$lang:tt $($b:tt)*] [$($c:tt)+] $($tt:tt)*) => {
|
||||||
|
docify!([$($b)*] [$($c)+, "\n\n```", stringify!($lang), "\n"] $($tt)*);
|
||||||
|
};
|
||||||
|
|
||||||
|
([@fence $($b:tt)*] [$($c:tt)+] $($tt:tt)*) => {
|
||||||
|
docify!([$($b)*] [$($c)+, "\n\n```\n"] $($tt)*);
|
||||||
|
};
|
||||||
|
|
||||||
|
([@{$($a:tt),*}! $($b:tt)*] [$($c:tt)+] $($tt:tt)*) => {
|
||||||
|
docify!([$($b)*] [$($c)+, $($a),*] $($tt)*);
|
||||||
|
};
|
||||||
|
|
||||||
|
([@{$($a:tt),*} $($b:tt)*] [$($c:tt)+] $($tt:tt)*) => {
|
||||||
|
docify!([$($b)*] [$($c)+, $($a),*, " "] $($tt)*);
|
||||||
|
};
|
||||||
|
|
||||||
|
([@code{$($a:tt)+}! $($b:tt)*] [$($c:tt)+] $($tt:tt)*) => {
|
||||||
|
docify!([$($b)*] [$($c)+, "`", $(stringify!($a)),*, "`"] $($tt)*);
|
||||||
|
};
|
||||||
|
|
||||||
|
([@code{$($a:tt)+} $($b:tt)*] [$($c:tt)+] $($tt:tt)*) => {
|
||||||
|
docify!([$($b)*] [$($c)+, "`", $(stringify!($a)),*, "` "] $($tt)*);
|
||||||
|
};
|
||||||
|
|
||||||
|
([@[$($a:tt)*]! $($b:tt)*] [$($c:tt)+] $($tt:tt)*) => {
|
||||||
|
docify!([$($b)*] [$($c)+, $(stringify!($a)),*] $($tt)*);
|
||||||
|
};
|
||||||
|
|
||||||
|
([@[$($a:tt)*] $($b:tt)*] [$($c:tt)+] $($tt:tt)*) => {
|
||||||
|
docify!([$($b)*] [$($c)+, $(stringify!($a)),*, " "] $($tt)*);
|
||||||
|
};
|
||||||
|
|
||||||
|
([@nl $($b:tt)*] [$($c:tt)+] $($tt:tt)*) => {
|
||||||
|
docify!([$($b)*] [$($c)+, "\n"] $($tt)*);
|
||||||
|
};
|
||||||
|
|
||||||
|
(@punct [$a:tt $p:tt $($b:tt)*] [$($c:tt)+] $($tt:tt)*) => {
|
||||||
|
docify!([$($b)*] [$($c)+, stringify!($a), stringify!($p), " "] $($tt)*);
|
||||||
|
};
|
||||||
|
|
||||||
|
(@upunct [$a:tt $p:tt $($b:tt)*] [$($c:tt)+] $($tt:tt)*) => {
|
||||||
|
docify!([$($b)*] [$($c)+, stringify!($a), stringify!($p)] $($tt)*);
|
||||||
|
};
|
||||||
|
|
||||||
|
([$a:tt . $($b:tt)*] $($rest:tt)+) => { docify!(@punct [$a . $($b)*] $($rest)+); };
|
||||||
|
([$a:tt , $($b:tt)*] $($rest:tt)+) => { docify!(@punct [$a , $($b)*] $($rest)+); };
|
||||||
|
([$a:tt ; $($b:tt)*] $($rest:tt)+) => { docify!(@punct [$a ; $($b)*] $($rest)+); };
|
||||||
|
([$a:tt : $($b:tt)*] $($rest:tt)+) => { docify!(@punct [$a : $($b)*] $($rest)+); };
|
||||||
|
([$a:tt ! $($b:tt)*] $($rest:tt)+) => { docify!(@punct [$a ! $($b)*] $($rest)+); };
|
||||||
|
([$a:tt ! $($b:tt)*] $($rest:tt)+) => { docify!(@punct [$a ! $($b)*] $($rest)+); };
|
||||||
|
|
||||||
|
([$a:tt :: $($b:tt)*] $($rest:tt)+) => { docify!(@upunct [$a :: $($b)*] $($rest)+); };
|
||||||
|
|
||||||
|
([$a:tt $($b:tt)*] [$($c:tt)+] $($tt:tt)*) => {
|
||||||
|
docify!([$($b)*] [$($c)+, stringify!($a), " "] $($tt)*);
|
||||||
|
};
|
||||||
|
|
||||||
|
([] [$($doc:expr),*] $($tt:tt)*) => {
|
||||||
|
docify!(concat!($($doc),*), $($tt)*);
|
||||||
|
};
|
||||||
|
|
||||||
|
($x:expr, $($tt:tt)*) => {
|
||||||
|
#[doc = $x]
|
||||||
|
$($tt)*
|
||||||
|
};
|
||||||
|
}
|
|
@ -46,46 +46,30 @@ macro_rules! content_types {
|
||||||
($($name:ident ($check:ident): $str:expr, $t:expr,
|
($($name:ident ($check:ident): $str:expr, $t:expr,
|
||||||
$s:expr $(; $k:expr => $v:expr)*,)+) => {
|
$s:expr $(; $k:expr => $v:expr)*,)+) => {
|
||||||
$(
|
$(
|
||||||
#[doc="Content-Type for <b>"] #[doc=$str] #[doc="</b>: <i>"]
|
docify!([
|
||||||
#[doc=$t] #[doc="/"] #[doc=$s]
|
Content Type for @{"**"}! @{$str}! @{"**"}!: @{"`"} @{$t}! @[/]! @{$s}!
|
||||||
$(#[doc="; "] #[doc=$k] #[doc=" = "] #[doc=$v])*
|
$(; @{$k}! @[=]! @{$v}!)* @{"`"}!.
|
||||||
#[doc="</i>"]
|
];
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
pub const $name: ContentType = ContentType(MediaType::$name);
|
pub const $name: ContentType = ContentType(MediaType::$name);
|
||||||
|
);
|
||||||
)+
|
)+
|
||||||
};
|
}}
|
||||||
}
|
|
||||||
|
|
||||||
impl ContentType {
|
macro_rules! from_extension {
|
||||||
/// Creates a new `ContentType` with top-level type `top` and subtype `sub`.
|
($($ext:expr => $name:ident,)*) => (
|
||||||
/// This should _only_ be used to construct uncommon or custom content
|
docify!([
|
||||||
/// types. Use an associated constant for everything else.
|
Returns the @[Content-Type] associated with the extension @code{ext}.
|
||||||
///
|
Not all extensions are recognized. If an extensions is not recognized,
|
||||||
/// # Example
|
@code{None} is returned. The currently recognized extensions are:
|
||||||
///
|
|
||||||
/// Create a custom `application/x-person` content type:
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// use rocket::http::ContentType;
|
|
||||||
///
|
|
||||||
/// let custom = ContentType::new("application", "x-person");
|
|
||||||
/// assert_eq!(custom.top(), "application");
|
|
||||||
/// assert_eq!(custom.sub(), "x-person");
|
|
||||||
/// ```
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn new<T, S>(top: T, sub: S) -> ContentType
|
|
||||||
where T: Into<Cow<'static, str>>, S: Into<Cow<'static, str>>
|
|
||||||
{
|
|
||||||
ContentType(MediaType::new(top, sub))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the Content-Type associated with the extension `ext` if the
|
@nl
|
||||||
/// extension is recognized. Not all extensions are recognized. If an
|
$(* @{$ext} - @{"`ContentType::"}! @[$name]! @{"`"} @nl)*
|
||||||
/// extensions is not recognized, then this method returns `None`. The
|
@nl
|
||||||
/// currently recognized extensions are txt, html, htm, xml, csv, js, css,
|
|
||||||
/// json, png, gif, bmp, jpeg, jpg, webp, svg, pdf, ttf, otf, woff, and
|
This list is likely to grow. Extensions are matched
|
||||||
/// woff2. Extensions are matched case-insensitively.
|
@[case-insensitively.]
|
||||||
///
|
];
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// Recognized content types:
|
/// Recognized content types:
|
||||||
|
@ -112,6 +96,96 @@ impl ContentType {
|
||||||
pub fn from_extension(ext: &str) -> Option<ContentType> {
|
pub fn from_extension(ext: &str) -> Option<ContentType> {
|
||||||
MediaType::from_extension(ext).map(ContentType)
|
MediaType::from_extension(ext).map(ContentType)
|
||||||
}
|
}
|
||||||
|
);)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! parse_flexible {
|
||||||
|
($($short:expr => $name:ident,)*) => (
|
||||||
|
docify!([
|
||||||
|
Flexibly parses @code{name} into a @code{ContentType}. The parse is
|
||||||
|
@[_flexible_] because, in addition to stricly correct content types, it
|
||||||
|
recognizes the following shorthands:
|
||||||
|
|
||||||
|
@nl
|
||||||
|
$(* $short - @{"`ContentType::"}! @[$name]! @{"`"} @nl)*
|
||||||
|
@nl
|
||||||
|
];
|
||||||
|
/// For regular parsing, use the
|
||||||
|
/// [`ContentType::from_str()`](#impl-FromStr) method.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// Using a shorthand:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use rocket::http::ContentType;
|
||||||
|
///
|
||||||
|
/// let html = ContentType::parse_flexible("html");
|
||||||
|
/// assert_eq!(html, Some(ContentType::HTML));
|
||||||
|
///
|
||||||
|
/// let json = ContentType::parse_flexible("json");
|
||||||
|
/// assert_eq!(json, Some(ContentType::JSON));
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Using the full content-type:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use rocket::http::ContentType;
|
||||||
|
///
|
||||||
|
/// let html = ContentType::parse_flexible("text/html; charset=utf-8");
|
||||||
|
/// assert_eq!(html, Some(ContentType::HTML));
|
||||||
|
///
|
||||||
|
/// let json = ContentType::parse_flexible("application/json");
|
||||||
|
/// assert_eq!(json, Some(ContentType::JSON));
|
||||||
|
///
|
||||||
|
/// let custom = ContentType::parse_flexible("application/x+custom");
|
||||||
|
/// assert_eq!(custom, Some(ContentType::new("application", "x+custom")));
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// An unrecognized content-type:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use rocket::http::ContentType;
|
||||||
|
///
|
||||||
|
/// let foo = ContentType::parse_flexible("foo");
|
||||||
|
/// assert_eq!(foo, None);
|
||||||
|
///
|
||||||
|
/// let bar = ContentType::parse_flexible("foo/bar/baz");
|
||||||
|
/// assert_eq!(bar, None);
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn parse_flexible(name: &str) -> Option<ContentType> {
|
||||||
|
MediaType::parse_flexible(name).map(ContentType)
|
||||||
|
}
|
||||||
|
);)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContentType {
|
||||||
|
/// Creates a new `ContentType` with top-level type `top` and subtype `sub`.
|
||||||
|
/// This should _only_ be used to construct uncommon or custom content
|
||||||
|
/// types. Use an associated constant for everything else.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// Create a custom `application/x-person` content type:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use rocket::http::ContentType;
|
||||||
|
///
|
||||||
|
/// let custom = ContentType::new("application", "x-person");
|
||||||
|
/// assert_eq!(custom.top(), "application");
|
||||||
|
/// assert_eq!(custom.sub(), "x-person");
|
||||||
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn new<T, S>(top: T, sub: S) -> ContentType
|
||||||
|
where T: Into<Cow<'static, str>>, S: Into<Cow<'static, str>>
|
||||||
|
{
|
||||||
|
ContentType(MediaType::new(top, sub))
|
||||||
|
}
|
||||||
|
|
||||||
|
known_shorthands!(parse_flexible);
|
||||||
|
|
||||||
|
known_extensions!(from_extension);
|
||||||
|
|
||||||
/// Creates a new `ContentType` with top-level type `top`, subtype `sub`,
|
/// Creates a new `ContentType` with top-level type `top`, subtype `sub`,
|
||||||
/// and parameters `ps`. This should _only_ be used to construct uncommon or
|
/// and parameters `ps`. This should _only_ be used to construct uncommon or
|
||||||
|
|
|
@ -5,7 +5,7 @@ macro_rules! known_media_types {
|
||||||
HTML (is_html): "HTML", "text", "html" ; "charset" => "utf-8",
|
HTML (is_html): "HTML", "text", "html" ; "charset" => "utf-8",
|
||||||
Plain (is_plain): "plain text", "text", "plain" ; "charset" => "utf-8",
|
Plain (is_plain): "plain text", "text", "plain" ; "charset" => "utf-8",
|
||||||
JSON (is_json): "JSON", "application", "json",
|
JSON (is_json): "JSON", "application", "json",
|
||||||
MsgPack (is_msgpack): "MessagePack", "application", "msgpack",
|
MsgPack (is_msgpack): "MsgPack", "application", "msgpack",
|
||||||
Form (is_form): "forms", "application", "x-www-form-urlencoded",
|
Form (is_form): "forms", "application", "x-www-form-urlencoded",
|
||||||
JavaScript (is_javascript): "JavaScript", "application", "javascript",
|
JavaScript (is_javascript): "JavaScript", "application", "javascript",
|
||||||
CSS (is_css): "CSS", "text", "css" ; "charset" => "utf-8",
|
CSS (is_css): "CSS", "text", "css" ; "charset" => "utf-8",
|
||||||
|
@ -51,3 +51,19 @@ macro_rules! known_extensions {
|
||||||
"woff2" => WOFF2,
|
"woff2" => WOFF2,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! known_shorthands {
|
||||||
|
($cont:ident) => ($cont! {
|
||||||
|
"any" => Any,
|
||||||
|
"binary" => Binary,
|
||||||
|
"html" => HTML,
|
||||||
|
"plain" => Plain,
|
||||||
|
"json" => JSON,
|
||||||
|
"msgpack" => MsgPack,
|
||||||
|
"form" => Form,
|
||||||
|
"js" => JavaScript,
|
||||||
|
"css" => CSS,
|
||||||
|
"multipart" => FormData,
|
||||||
|
"xml" => XML,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -107,10 +107,10 @@ macro_rules! media_types {
|
||||||
($($name:ident ($check:ident): $str:expr, $t:expr,
|
($($name:ident ($check:ident): $str:expr, $t:expr,
|
||||||
$s:expr $(; $k:expr => $v:expr)*,)+) => {
|
$s:expr $(; $k:expr => $v:expr)*,)+) => {
|
||||||
$(
|
$(
|
||||||
#[doc="Media type for <b>"] #[doc=$str] #[doc="</b>: <i>"]
|
docify!([
|
||||||
#[doc=$t] #[doc="/"] #[doc=$s]
|
Media Type for @{"**"}! @{$str}! @{"**"}!: @{"`"} @{$t}! @[/]! @{$s}!
|
||||||
$(#[doc="; "] #[doc=$k] #[doc=" = "] #[doc=$v])*
|
$(; @{$k}! @[=]! @{$v}!)* @{"`"}!.
|
||||||
#[doc="</i>"]
|
];
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
pub const $name: MediaType = MediaType {
|
pub const $name: MediaType = MediaType {
|
||||||
source: Source::Known(concat!($t, "/", $s, $("; ", $k, "=", $v),*)),
|
source: Source::Known(concat!($t, "/", $s, $("; ", $k, "=", $v),*)),
|
||||||
|
@ -118,34 +118,48 @@ macro_rules! media_types {
|
||||||
sub: media_str!($s),
|
sub: media_str!($s),
|
||||||
params: MediaParams::Static(&[$((media_str!($k), media_str!($v))),*])
|
params: MediaParams::Static(&[$((media_str!($k), media_str!($v))),*])
|
||||||
};
|
};
|
||||||
|
);
|
||||||
|
)+
|
||||||
|
|
||||||
#[doc="Returns `true` if `self` is the media type for <b>"]
|
/// Returns `true` if this MediaType is known to Rocket. In other words,
|
||||||
#[doc=$str]
|
/// returns `true` if there is an associated constant for `self`.
|
||||||
#[doc="</b>, "]
|
pub fn is_known(&self) -> bool {
|
||||||
/// without considering parameters.
|
if let Source::Known(_) = self.source {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$(if self.$check() { return true })+
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
$(
|
||||||
|
docify!([
|
||||||
|
Returns @code{true} if the @[top-level] and sublevel types of
|
||||||
|
@code{self} are the same as those of @{"`MediaType::"}! $name
|
||||||
|
@{"`"}!.
|
||||||
|
];
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn $check(&self) -> bool {
|
pub fn $check(&self) -> bool {
|
||||||
*self == MediaType::$name
|
*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 {
|
macro_rules! from_extension {
|
||||||
($($ext:expr => $name:ident,)*) => (
|
($($ext:expr => $name:ident,)*) => (
|
||||||
/// Returns the Media-Type associated with the extension `ext`. Not all
|
docify!([
|
||||||
/// extensions are recognized. If an extensions is not recognized,
|
Returns the @[Media-Type] associated with the extension @code{ext}. Not
|
||||||
/// `None` is returned. The currently recognized extensions are
|
all extensions are recognized. If an extensions is not recognized,
|
||||||
$(#[doc=$ext]#[doc=","])*
|
@code{None} is returned. The currently recognized extensions are:
|
||||||
/// and is likely to grow. Extensions are matched case-insensitively.
|
|
||||||
///
|
@nl
|
||||||
|
$(* @{$ext} - @{"`MediaType::"}! @[$name]! @{"`"} @nl)*
|
||||||
|
@nl
|
||||||
|
|
||||||
|
This list is likely to grow. Extensions are matched
|
||||||
|
@[case-insensitively.]
|
||||||
|
];
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// Recognized media types:
|
/// Recognized media types:
|
||||||
|
@ -174,7 +188,70 @@ macro_rules! from_extension {
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
);)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! parse_flexible {
|
||||||
|
($($short:expr => $name:ident,)*) => (
|
||||||
|
docify!([
|
||||||
|
Flexibly parses @code{name} into a @code{MediaType}. The parse is
|
||||||
|
@[_flexible_] because, in addition to stricly correct media types, it
|
||||||
|
recognizes the following shorthands:
|
||||||
|
|
||||||
|
@nl
|
||||||
|
$(* $short - @{"`MediaType::"}! @[$name]! @{"`"} @nl)*
|
||||||
|
@nl
|
||||||
|
];
|
||||||
|
/// For regular parsing, use the
|
||||||
|
/// [`MediaType::from_str()`](#impl-FromStr) method.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// Using a shorthand:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use rocket::http::MediaType;
|
||||||
|
///
|
||||||
|
/// let html = MediaType::parse_flexible("html");
|
||||||
|
/// assert_eq!(html, Some(MediaType::HTML));
|
||||||
|
///
|
||||||
|
/// let json = MediaType::parse_flexible("json");
|
||||||
|
/// assert_eq!(json, Some(MediaType::JSON));
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Using the full media type:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use rocket::http::MediaType;
|
||||||
|
///
|
||||||
|
/// let html = MediaType::parse_flexible("text/html; charset=utf-8");
|
||||||
|
/// assert_eq!(html, Some(MediaType::HTML));
|
||||||
|
///
|
||||||
|
/// let json = MediaType::parse_flexible("application/json");
|
||||||
|
/// assert_eq!(json, Some(MediaType::JSON));
|
||||||
|
///
|
||||||
|
/// let custom = MediaType::parse_flexible("application/x+custom");
|
||||||
|
/// assert_eq!(custom, Some(MediaType::new("application", "x+custom")));
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// An unrecognized media type:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use rocket::http::MediaType;
|
||||||
|
///
|
||||||
|
/// let foo = MediaType::parse_flexible("foo");
|
||||||
|
/// assert_eq!(foo, None);
|
||||||
|
///
|
||||||
|
/// let bar = MediaType::parse_flexible("foo/bar/baz");
|
||||||
|
/// assert_eq!(bar, None);
|
||||||
|
/// ```
|
||||||
|
pub fn parse_flexible(name: &str) -> Option<MediaType> {
|
||||||
|
match name {
|
||||||
|
$(x if uncased_eq(x, $short) => Some(MediaType::$name)),*,
|
||||||
|
_ => MediaType::from_str(name).ok(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MediaType {
|
impl MediaType {
|
||||||
|
@ -240,7 +317,6 @@ impl MediaType {
|
||||||
IndexedStr::Concrete(val.into())
|
IndexedStr::Concrete(val.into())
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
||||||
MediaType {
|
MediaType {
|
||||||
source: Source::None,
|
source: Source::None,
|
||||||
top: IndexedStr::Concrete(top.into()),
|
top: IndexedStr::Concrete(top.into()),
|
||||||
|
@ -249,6 +325,8 @@ impl MediaType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
known_shorthands!(parse_flexible);
|
||||||
|
|
||||||
known_extensions!(from_extension);
|
known_extensions!(from_extension);
|
||||||
|
|
||||||
/// Returns the top-level type for this media type. The return type,
|
/// Returns the top-level type for this media type. The return type,
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#![feature(plugin, decl_macro)]
|
#![feature(plugin, decl_macro)]
|
||||||
#![feature(never_type)]
|
#![feature(never_type)]
|
||||||
#![feature(try_trait)]
|
#![feature(try_trait)]
|
||||||
|
#![recursion_limit="256"]
|
||||||
|
|
||||||
#![plugin(pear_codegen)]
|
#![plugin(pear_codegen)]
|
||||||
|
|
||||||
|
@ -118,6 +119,7 @@ extern crate isatty;
|
||||||
#[cfg(test)] #[macro_use] extern crate lazy_static;
|
#[cfg(test)] #[macro_use] extern crate lazy_static;
|
||||||
|
|
||||||
#[doc(hidden)] #[macro_use] pub mod logger;
|
#[doc(hidden)] #[macro_use] pub mod logger;
|
||||||
|
#[macro_use] mod docify;
|
||||||
pub mod local;
|
pub mod local;
|
||||||
pub mod http;
|
pub mod http;
|
||||||
pub mod request;
|
pub mod request;
|
||||||
|
|
|
@ -90,4 +90,3 @@ ctrs! {
|
||||||
Css: CSS, "CSS", "text/css",
|
Css: CSS, "CSS", "text/css",
|
||||||
JavaScript: JavaScript, "JavaScript", "application/javascript"
|
JavaScript: JavaScript, "JavaScript", "application/javascript"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue