Use MediaType instead of ContentType for Route format.

This commit is contained in:
Sergio Benitez 2017-03-28 00:12:59 -07:00
parent 9160483554
commit 1fb1cdfc58
8 changed files with 107 additions and 109 deletions

View File

@ -14,7 +14,7 @@ use syntax::ext::build::AstBuilder;
use syntax::parse::token;
use syntax::ptr::P;
use rocket::http::{Method, ContentType};
use rocket::http::{Method, MediaType};
fn method_to_path(ecx: &ExtCtxt, method: Method) -> Path {
quote_enum!(ecx, method => ::rocket::http::Method {
@ -22,21 +22,19 @@ fn method_to_path(ecx: &ExtCtxt, method: Method) -> Path {
})
}
fn content_type_to_expr(ecx: &ExtCtxt, ct: Option<ContentType>) -> Option<P<Expr>> {
fn media_type_to_expr(ecx: &ExtCtxt, ct: Option<MediaType>) -> Option<P<Expr>> {
ct.map(|ct| {
let (top, sub) = (ct.top().as_str(), ct.sub().as_str());
quote_expr!(ecx, ::rocket::http::ContentType(
::rocket::http::MediaType {
source: None,
top: ::rocket::http::IndexedStr::Concrete(
::std::borrow::Cow::Borrowed($top)
),
sub: ::rocket::http::IndexedStr::Concrete(
::std::borrow::Cow::Borrowed($sub)
),
params: ::rocket::http::MediaParams::Static(&[])
},
))
quote_expr!(ecx, ::rocket::http::MediaType {
source: None,
top: ::rocket::http::IndexedStr::Concrete(
::std::borrow::Cow::Borrowed($top)
),
sub: ::rocket::http::IndexedStr::Concrete(
::std::borrow::Cow::Borrowed($sub)
),
params: ::rocket::http::MediaParams::Static(&[])
})
})
}
@ -224,10 +222,10 @@ impl RouteGenerateExt for RouteParams {
let path = &self.uri.node.as_str();
let method = method_to_path(ecx, self.method.node);
let format = self.format.as_ref().map(|kv| kv.value().clone());
let content_type = option_as_expr(ecx, &content_type_to_expr(ecx, format));
let media_type = option_as_expr(ecx, &media_type_to_expr(ecx, format));
let rank = option_as_expr(ecx, &self.rank);
(path, method, content_type, rank)
(path, method, media_type, rank)
}
}
@ -267,7 +265,7 @@ fn generic_route_decorator(known_method: Option<Spanned<Method>>,
// Generate and emit the static route info that uses the just generated
// function as its handler. A proper Rocket route will be created from this.
let struct_name = user_fn_name.prepend(ROUTE_STRUCT_PREFIX);
let (path, method, content_type, rank) = route.explode(ecx);
let (path, method, media_type, rank) = route.explode(ecx);
let static_route_info_item = quote_item!(ecx,
#[allow(non_upper_case_globals)]
pub static $struct_name: ::rocket::StaticRouteInfo =
@ -275,7 +273,7 @@ fn generic_route_decorator(known_method: Option<Spanned<Method>>,
method: $method,
path: $path,
handler: $route_fn_name,
format: $content_type,
format: $media_type,
rank: $rank,
};
).expect("static route info");

View File

@ -9,7 +9,7 @@ use utils::{span, MetaItemExt, SpanExt, is_valid_ident};
use super::{Function, ParamIter};
use super::keyvalue::KVSpanned;
use super::uri::validate_uri;
use rocket::http::{Method, ContentType};
use rocket::http::{Method, MediaType};
use rocket::http::uri::URI;
/// This structure represents the parsed `route` attribute.
@ -25,7 +25,7 @@ pub struct RouteParams {
pub uri: Spanned<URI<'static>>,
pub data_param: Option<KVSpanned<Ident>>,
pub query_param: Option<Spanned<Ident>>,
pub format: Option<KVSpanned<ContentType>>,
pub format: Option<KVSpanned<MediaType>>,
pub rank: Option<KVSpanned<isize>>,
}
@ -258,25 +258,25 @@ fn parse_rank(ecx: &ExtCtxt, kv: &KVSpanned<LitKind>) -> isize {
-1
}
fn parse_format(ecx: &ExtCtxt, kv: &KVSpanned<LitKind>) -> ContentType {
fn parse_format(ecx: &ExtCtxt, kv: &KVSpanned<LitKind>) -> MediaType {
if let LitKind::Str(ref s, _) = *kv.value() {
if let Ok(ct) = ContentType::from_str(&s.as_str()) {
if let Ok(ct) = MediaType::from_str(&s.as_str()) {
if !ct.is_known() {
let msg = format!("'{}' is not a known content-type", s);
let msg = format!("'{}' is not a known media type", s);
ecx.span_warn(kv.value.span, &msg);
}
return ct;
} else {
ecx.span_err(kv.value.span, "malformed content-type");
ecx.span_err(kv.value.span, "malformed media type");
}
}
ecx.struct_span_err(kv.span, r#"`format` must be a "content/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
the key is `format` and the value is a string representing the
content-type accepted. e.g: format = "application/json""#)
media type accepted. e.g: format = "application/json""#)
.emit();
ContentType::Any
MediaType::Any
}

View File

@ -12,7 +12,7 @@ fn get1() -> &'static str { "hi" }
#[get(path = "/", rank = "2")] //~ ERROR must be an int
fn get2() -> &'static str { "hi" }
#[get(path = "/", format = 100)] //~ ERROR must be a "content/type"
#[get(path = "/", format = 100)] //~ ERROR must be a "media/type"
fn get3() -> &'static str { "hi" }
fn main() {

View File

@ -4,35 +4,35 @@
extern crate rocket;
#[get("/", format = "applicationx-custom")] //~ ERROR malformed
//~^ ERROR `format` must be a "content/type"
//~^ ERROR `format` must be a "media/type"
fn one() -> &'static str { "hi" }
#[get("/", format = "")] //~ ERROR malformed
//~^ ERROR `format` must be a "content/type"
//~^ ERROR `format` must be a "media/type"
fn two() -> &'static str { "hi" }
#[get("/", format = "//")] //~ ERROR malformed
//~^ ERROR `format` must be a "content/type"
//~^ ERROR `format` must be a "media/type"
fn three() -> &'static str { "hi" }
#[get("/", format = "/")] //~ ERROR malformed
//~^ ERROR `format` must be a "content/type"
//~^ ERROR `format` must be a "media/type"
fn four() -> &'static str { "hi" }
#[get("/", format = "a/")] //~ ERROR malformed
//~^ ERROR `format` must be a "content/type"
//~^ ERROR `format` must be a "media/type"
fn five() -> &'static str { "hi" }
#[get("/", format = "/a")] //~ ERROR malformed
//~^ ERROR `format` must be a "content/type"
//~^ ERROR `format` must be a "media/type"
fn six() -> &'static str { "hi" }
#[get("/", format = "/a/")] //~ ERROR malformed
//~^ ERROR `format` must be a "content/type"
//~^ ERROR `format` must be a "media/type"
fn seven() -> &'static str { "hi" }
#[get("/", format = "a/b/")] //~ ERROR malformed
//~^ ERROR `format` must be a "content/type"
//~^ ERROR `format` must be a "media/type"
fn eight() -> &'static str { "hi" }
fn main() { }

View File

@ -3,13 +3,13 @@
extern crate rocket;
#[get("/", format = "application/x-custom")] //~ WARNING not a known content-type
#[get("/", format = "application/x-custom")] //~ WARNING not a known media type
fn one() -> &'static str { "hi" }
#[get("/", format = "x-custom/plain")] //~ WARNING not a known content-type
#[get("/", format = "x-custom/plain")] //~ WARNING not a known media type
fn two() -> &'static str { "hi" }
#[get("/", format = "x-custom/x-custom")] //~ WARNING not a known content-type
#[get("/", format = "x-custom/x-custom")] //~ WARNING not a known media type
fn three() -> &'static str { "hi" }
// Make the test fail here so we can actually check for the warnings above.

View File

@ -1,10 +1,10 @@
use handler::{Handler, ErrorHandler};
use http::{Method, ContentType};
use http::{Method, MediaType};
pub struct StaticRouteInfo {
pub method: Method,
pub path: &'static str,
pub format: Option<ContentType>,
pub format: Option<MediaType>,
pub handler: Handler,
pub rank: Option<isize>,
}

View File

@ -1,7 +1,7 @@
use super::Route;
use http::uri::URI;
use http::ContentType;
use http::MediaType;
use request::Request;
/// The Collider trait is used to determine if two items that can be routed on
@ -71,8 +71,8 @@ impl<'a, 'b> Collider<URI<'b>> for URI<'a> {
}
}
impl Collider for ContentType {
fn collides_with(&self, other: &ContentType) -> bool {
impl Collider for MediaType {
fn collides_with(&self, other: &MediaType) -> bool {
let collide = |a, b| a == "*" || b == "*" || a == b;
collide(self.top(), other.top()) && collide(self.sub(), other.sub())
}
@ -93,7 +93,7 @@ impl Collider for Route {
&& self.rank == b.rank
&& self.path.collides_with(&b.path)
&& match (self.format.as_ref(), b.format.as_ref()) {
(Some(ct_a), Some(ct_b)) => ct_a.collides_with(ct_b),
(Some(mt_a), Some(mt_b)) => mt_a.collides_with(mt_b),
(Some(_), None) => true,
(None, Some(_)) => true,
(None, None) => true
@ -115,7 +115,7 @@ impl<'r> Collider<Request<'r>> for Route {
&& self.path.query().map_or(true, |_| req.uri().query().is_some())
// FIXME: On payload requests, check Content-Type, else Accept.
&& match (req.content_type().as_ref(), self.format.as_ref()) {
(Some(ct_a), Some(ct_b)) => ct_a.collides_with(ct_b),
(Some(mt_a), Some(mt_b)) => mt_a.collides_with(mt_b),
(Some(_), None) => true,
(None, Some(_)) => false,
(None, None) => true
@ -132,7 +132,7 @@ mod tests {
use data::Data;
use handler::Outcome;
use router::route::Route;
use http::{Method, ContentType};
use http::{Method, MediaType, ContentType};
use http::uri::URI;
use http::Method::*;
@ -301,41 +301,41 @@ mod tests {
assert!(!s_s_collide("/a/hi/<a..>", "/a/hi/"));
}
fn ct_ct_collide(ct1: &str, ct2: &str) -> bool {
let ct_a = ContentType::from_str(ct1).expect(ct1);
let ct_b = ContentType::from_str(ct2).expect(ct2);
ct_a.collides_with(&ct_b)
fn mt_mt_collide(mt1: &str, mt2: &str) -> bool {
let mt_a = MediaType::from_str(mt1).expect(mt1);
let mt_b = MediaType::from_str(mt2).expect(mt2);
mt_a.collides_with(&mt_b)
}
#[test]
fn test_content_type_colliions() {
assert!(ct_ct_collide("application/json", "application/json"));
assert!(ct_ct_collide("*/json", "application/json"));
assert!(ct_ct_collide("*/*", "application/json"));
assert!(ct_ct_collide("application/*", "application/json"));
assert!(ct_ct_collide("application/*", "*/json"));
assert!(ct_ct_collide("something/random", "something/random"));
assert!(mt_mt_collide("application/json", "application/json"));
assert!(mt_mt_collide("*/json", "application/json"));
assert!(mt_mt_collide("*/*", "application/json"));
assert!(mt_mt_collide("application/*", "application/json"));
assert!(mt_mt_collide("application/*", "*/json"));
assert!(mt_mt_collide("something/random", "something/random"));
assert!(!ct_ct_collide("text/*", "application/*"));
assert!(!ct_ct_collide("*/text", "*/json"));
assert!(!ct_ct_collide("*/text", "application/test"));
assert!(!ct_ct_collide("something/random", "something_else/random"));
assert!(!ct_ct_collide("something/random", "*/else"));
assert!(!ct_ct_collide("*/random", "*/else"));
assert!(!ct_ct_collide("something/*", "random/else"));
assert!(!mt_mt_collide("text/*", "application/*"));
assert!(!mt_mt_collide("*/text", "*/json"));
assert!(!mt_mt_collide("*/text", "application/test"));
assert!(!mt_mt_collide("something/random", "something_else/random"));
assert!(!mt_mt_collide("something/random", "*/else"));
assert!(!mt_mt_collide("*/random", "*/else"));
assert!(!mt_mt_collide("something/*", "random/else"));
}
fn r_ct_ct_collide<S1, S2>(m1: Method, ct1: S1, m2: Method, ct2: S2) -> bool
fn r_mt_mt_collide<S1, S2>(m1: Method, mt1: S1, m2: Method, mt2: S2) -> bool
where S1: Into<Option<&'static str>>, S2: Into<Option<&'static str>>
{
let mut route_a = Route::new(m1, "/", dummy_handler);
if let Some(ct_str) = ct1.into() {
route_a.format = Some(ct_str.parse::<ContentType>().unwrap());
if let Some(mt_str) = mt1.into() {
route_a.format = Some(mt_str.parse::<MediaType>().unwrap());
}
let mut route_b = Route::new(m2, "/", dummy_handler);
if let Some(ct_str) = ct2.into() {
route_b.format = Some(ct_str.parse::<ContentType>().unwrap());
if let Some(mt_str) = mt2.into() {
route_b.format = Some(mt_str.parse::<MediaType>().unwrap());
}
route_a.collides_with(&route_b)
@ -343,61 +343,61 @@ mod tests {
#[test]
fn test_route_content_type_colliions() {
assert!(r_ct_ct_collide(Get, "application/json", Get, "application/json"));
assert!(r_ct_ct_collide(Get, "*/json", Get, "application/json"));
assert!(r_ct_ct_collide(Get, "*/json", Get, "application/*"));
assert!(r_ct_ct_collide(Get, "text/html", Get, "text/*"));
assert!(r_ct_ct_collide(Get, "any/thing", Get, "*/*"));
assert!(r_mt_mt_collide(Get, "application/json", Get, "application/json"));
assert!(r_mt_mt_collide(Get, "*/json", Get, "application/json"));
assert!(r_mt_mt_collide(Get, "*/json", Get, "application/*"));
assert!(r_mt_mt_collide(Get, "text/html", Get, "text/*"));
assert!(r_mt_mt_collide(Get, "any/thing", Get, "*/*"));
assert!(r_ct_ct_collide(Get, None, Get, "text/*"));
assert!(r_ct_ct_collide(Get, None, Get, "text/html"));
assert!(r_ct_ct_collide(Get, None, Get, "*/*"));
assert!(r_ct_ct_collide(Get, "text/html", Get, None));
assert!(r_ct_ct_collide(Get, "*/*", Get, None));
assert!(r_ct_ct_collide(Get, "application/json", Get, None));
assert!(r_mt_mt_collide(Get, None, Get, "text/*"));
assert!(r_mt_mt_collide(Get, None, Get, "text/html"));
assert!(r_mt_mt_collide(Get, None, Get, "*/*"));
assert!(r_mt_mt_collide(Get, "text/html", Get, None));
assert!(r_mt_mt_collide(Get, "*/*", Get, None));
assert!(r_mt_mt_collide(Get, "application/json", Get, None));
assert!(!r_ct_ct_collide(Get, "text/html", Get, "application/*"));
assert!(!r_ct_ct_collide(Get, "application/html", Get, "text/*"));
assert!(!r_ct_ct_collide(Get, "*/json", Get, "text/html"));
assert!(!r_ct_ct_collide(Get, "text/html", Get, "text/css"));
assert!(!r_mt_mt_collide(Get, "text/html", Get, "application/*"));
assert!(!r_mt_mt_collide(Get, "application/html", Get, "text/*"));
assert!(!r_mt_mt_collide(Get, "*/json", Get, "text/html"));
assert!(!r_mt_mt_collide(Get, "text/html", Get, "text/css"));
}
fn req_route_ct_collide<S1, S2>(m1: Method, ct1: S1, m2: Method, ct2: S2) -> bool
fn req_route_mt_collide<S1, S2>(m1: Method, mt1: S1, m2: Method, mt2: S2) -> bool
where S1: Into<Option<&'static str>>, S2: Into<Option<&'static str>>
{
let mut req = Request::new(m1, "/");
if let Some(ct_str) = ct1.into() {
req.replace_header(ct_str.parse::<ContentType>().unwrap());
if let Some(mt_str) = mt1.into() {
req.replace_header(mt_str.parse::<ContentType>().unwrap());
}
let mut route = Route::new(m2, "/", dummy_handler);
if let Some(ct_str) = ct2.into() {
route.format = Some(ct_str.parse::<ContentType>().unwrap());
if let Some(mt_str) = mt2.into() {
route.format = Some(mt_str.parse::<MediaType>().unwrap());
}
route.collides_with(&req)
}
#[test]
fn test_req_route_ct_collisions() {
assert!(req_route_ct_collide(Get, "application/json", Get, "application/json"));
assert!(req_route_ct_collide(Get, "application/json", Get, "application/*"));
assert!(req_route_ct_collide(Get, "application/json", Get, "*/json"));
assert!(req_route_ct_collide(Get, "text/html", Get, "text/html"));
assert!(req_route_ct_collide(Get, "text/html", Get, "*/*"));
fn test_req_route_mt_collisions() {
assert!(req_route_mt_collide(Get, "application/json", Get, "application/json"));
assert!(req_route_mt_collide(Get, "application/json", Get, "application/*"));
assert!(req_route_mt_collide(Get, "application/json", Get, "*/json"));
assert!(req_route_mt_collide(Get, "text/html", Get, "text/html"));
assert!(req_route_mt_collide(Get, "text/html", Get, "*/*"));
assert!(req_route_ct_collide(Get, "text/html", Get, None));
assert!(req_route_ct_collide(Get, None, Get, None));
assert!(req_route_ct_collide(Get, "application/json", Get, None));
assert!(req_route_ct_collide(Get, "x-custom/anything", Get, None));
assert!(req_route_mt_collide(Get, "text/html", Get, None));
assert!(req_route_mt_collide(Get, None, Get, None));
assert!(req_route_mt_collide(Get, "application/json", Get, None));
assert!(req_route_mt_collide(Get, "x-custom/anything", Get, None));
assert!(!req_route_ct_collide(Get, "application/json", Get, "text/html"));
assert!(!req_route_ct_collide(Get, "application/json", Get, "text/*"));
assert!(!req_route_ct_collide(Get, "application/json", Get, "*/xml"));
assert!(!req_route_mt_collide(Get, "application/json", Get, "text/html"));
assert!(!req_route_mt_collide(Get, "application/json", Get, "text/*"));
assert!(!req_route_mt_collide(Get, "application/json", Get, "*/xml"));
assert!(!req_route_ct_collide(Get, None, Get, "text/html"));
assert!(!req_route_ct_collide(Get, None, Get, "*/*"));
assert!(!req_route_ct_collide(Get, None, Get, "application/json"));
assert!(!req_route_mt_collide(Get, None, Get, "text/html"));
assert!(!req_route_mt_collide(Get, None, Get, "*/*"));
assert!(!req_route_mt_collide(Get, None, Get, "application/json"));
}
fn req_route_path_collide(a: &'static str, b: &'static str) -> bool {

View File

@ -6,10 +6,10 @@ use term_painter::Color::*;
use codegen::StaticRouteInfo;
use handler::Handler;
use http::{Method, ContentType};
use http::{Method, MediaType};
use http::uri::URI;
/// A route: a method, its handler, path, rank, and format/content type.
/// A route: a method, its handler, path, rank, and format/media type.
pub struct Route {
/// The method this route matches against.
pub method: Method,
@ -19,8 +19,8 @@ pub struct Route {
pub path: URI<'static>,
/// The rank of this route. Lower ranks have higher priorities.
pub rank: isize,
/// The Content-Type this route matches against.
pub format: Option<ContentType>,
/// The media type this route matches against.
pub format: Option<MediaType>,
}
#[inline(always)]