mirror of https://github.com/rwf2/Rocket.git
Add `rank` to route attribute. Macrofy is_some ContentType methods.
This commit is contained in:
parent
2fe13b2fe8
commit
8b99016af4
|
@ -4,7 +4,7 @@
|
|||
extern crate rocket;
|
||||
extern crate serde_json;
|
||||
|
||||
use rocket::{Rocket, Request, Error};
|
||||
use rocket::{Rocket, Request, Error, ContentType};
|
||||
use rocket::response::JSON;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
@ -13,13 +13,15 @@ struct Person {
|
|||
age: i8,
|
||||
}
|
||||
|
||||
// FIXME: Change 'content' to 'format'. Look at 'accept' header to match.
|
||||
#[GET(path = "/<name>/<age>", content = "application/json")]
|
||||
fn hello(name: String, age: i8) -> JSON<String> {
|
||||
fn hello(content_type: ContentType, name: String, age: i8) -> JSON<String> {
|
||||
let person = Person {
|
||||
name: name,
|
||||
age: age,
|
||||
};
|
||||
|
||||
println!("ContentType: {}", content_type);
|
||||
JSON(serde_json::to_string(&person).unwrap())
|
||||
}
|
||||
|
||||
|
|
|
@ -9,12 +9,11 @@ fn hello(name: &str, age: i8) -> String {
|
|||
format!("Hello, {} year old named {}!", age, name)
|
||||
}
|
||||
|
||||
// FIXME: Add 'rank = 2'.
|
||||
#[GET(path = "/hello/<name>/<age>")]
|
||||
#[GET(path = "/hello/<name>/<age>", rank = "2")]
|
||||
fn hi(name: &str, age: &str) -> String {
|
||||
format!("Hi {}! You age ({}) is kind of funky.", name, age)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
Rocket::new("localhost", 8000).mount_and_launch("/", routes![hello, hi]);
|
||||
Rocket::new("localhost", 8000).mount_and_launch("/", routes![hi, hello]);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ pub struct StaticRouteInfo {
|
|||
pub path: &'static str,
|
||||
pub content_type: ContentType,
|
||||
pub handler: Handler,
|
||||
pub rank: Option<isize>,
|
||||
}
|
||||
|
||||
pub struct StaticCatchInfo {
|
||||
|
|
|
@ -4,14 +4,20 @@ use response::mime::{Param};
|
|||
use std::str::FromStr;
|
||||
use std::borrow::Borrow;
|
||||
use std::fmt;
|
||||
use self::TopLevel::{Text, Application};
|
||||
use self::SubLevel::{Json, Html};
|
||||
|
||||
use router::Collider;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ContentType(pub TopLevel, pub SubLevel, pub Option<Vec<Param>>);
|
||||
|
||||
macro_rules! is_some {
|
||||
($name:ident: $top:ident/$sub:ident) => {
|
||||
pub fn $name(&self) -> bool {
|
||||
self.0 == TopLevel::$top && self.1 == SubLevel::$sub
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl ContentType {
|
||||
#[inline(always)]
|
||||
pub fn new(t: TopLevel, s: SubLevel, params: Option<Vec<Param>>) -> ContentType {
|
||||
|
@ -28,14 +34,6 @@ impl ContentType {
|
|||
ContentType::of(TopLevel::Star, SubLevel::Star)
|
||||
}
|
||||
|
||||
pub fn is_json(&self) -> bool {
|
||||
self.0 == Application && self.1 == Json
|
||||
}
|
||||
|
||||
pub fn is_any(&self) -> bool {
|
||||
self.0 == TopLevel::Star && self.1 == SubLevel::Star
|
||||
}
|
||||
|
||||
pub fn is_ext(&self) -> bool {
|
||||
if let TopLevel::Ext(_) = self.0 {
|
||||
true
|
||||
|
@ -46,9 +44,10 @@ impl ContentType {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn is_html(&self) -> bool {
|
||||
self.0 == Text && self.1 == Html
|
||||
}
|
||||
is_some!(is_json: Application/Json);
|
||||
is_some!(is_xml: Application/Xml);
|
||||
is_some!(is_any: Star/Star);
|
||||
is_some!(is_html: Application/Html);
|
||||
}
|
||||
|
||||
impl Into<Mime> for ContentType {
|
||||
|
|
|
@ -33,6 +33,14 @@ impl Method {
|
|||
HyperMethod::Extension(_) => None
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the method supports a payload or not.
|
||||
pub fn supports_payload(&self) -> bool {
|
||||
match *self {
|
||||
Put | Post | Delete | Patch => true,
|
||||
Get | Head | Connect | Trace | Options => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Method {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use request::*;
|
||||
use method::Method;
|
||||
use std::fmt::Debug;
|
||||
use content_type::ContentType;
|
||||
|
||||
pub trait FromRequest<'r, 'c>: Sized {
|
||||
type Error: Debug;
|
||||
|
@ -17,7 +18,7 @@ impl<'r, 'c> FromRequest<'r, 'c> for &'r Request<'c> {
|
|||
}
|
||||
|
||||
impl<'r, 'c> FromRequest<'r, 'c> for Method {
|
||||
type Error = &'static str;
|
||||
type Error = ();
|
||||
|
||||
fn from_request(request: &'r Request<'c>) -> Result<Self, Self::Error> {
|
||||
Ok(request.method)
|
||||
|
@ -25,7 +26,7 @@ impl<'r, 'c> FromRequest<'r, 'c> for Method {
|
|||
}
|
||||
|
||||
impl<'r, 'c> FromRequest<'r, 'c> for Cookies {
|
||||
type Error = &'static str;
|
||||
type Error = ();
|
||||
|
||||
fn from_request(request: &'r Request<'c>) -> Result<Self, Self::Error> {
|
||||
match request.headers().get::<HyperCookie>() {
|
||||
|
@ -36,6 +37,14 @@ impl<'r, 'c> FromRequest<'r, 'c> for Cookies {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'r, 'c> FromRequest<'r, 'c> for ContentType {
|
||||
type Error = ();
|
||||
|
||||
fn from_request(request: &'r Request<'c>) -> Result<Self, Self::Error> {
|
||||
Ok(request.content_type())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, 'c, T: FromRequest<'r, 'c>> FromRequest<'r, 'c> for Option<T> {
|
||||
type Error = ();
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ impl<'a> Request<'a> {
|
|||
&self.headers
|
||||
}
|
||||
|
||||
// FIXME: This should be an Option. Not all requests have content types.
|
||||
pub fn content_type(&self) -> ContentType {
|
||||
let hyp_ct = self.headers().get::<header::ContentType>();
|
||||
hyp_ct.map_or(ContentType::any(), |ct| ContentType::from(&ct.0))
|
||||
|
|
|
@ -15,5 +15,6 @@ macro_rules! impl_data_type_responder {
|
|||
}
|
||||
|
||||
impl_data_type_responder!(JSON: Application/Json);
|
||||
impl_data_type_responder!(XML: Application/Xml);
|
||||
impl_data_type_responder!(HTML: Text/Html);
|
||||
impl_data_type_responder!(Plain: Text/Plain);
|
||||
|
|
|
@ -71,6 +71,10 @@ impl fmt::Display for Route {
|
|||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{} {}", Green.paint(&self.method), Blue.paint(&self.path))?;
|
||||
|
||||
if self.rank > 1 {
|
||||
write!(f, " [{}]", White.paint(&self.rank))?;
|
||||
}
|
||||
|
||||
if !self.content_type.is_any() {
|
||||
write!(f, " {}", Yellow.paint(&self.content_type))
|
||||
} else {
|
||||
|
@ -83,6 +87,10 @@ impl<'a> From<&'a StaticRouteInfo> for Route {
|
|||
fn from(info: &'a StaticRouteInfo) -> Route {
|
||||
let mut route = Route::new(info.method, info.path, info.handler);
|
||||
route.content_type = info.content_type.clone();
|
||||
if let Some(rank) = info.rank {
|
||||
route.rank = rank;
|
||||
}
|
||||
|
||||
route
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,6 +120,7 @@ pub struct RouteParams {
|
|||
pub path: KVSpanned<String>,
|
||||
pub form: Option<KVSpanned<String>>,
|
||||
pub content_type: KVSpanned<ContentType>,
|
||||
pub rank: Option<KVSpanned<isize>>,
|
||||
}
|
||||
|
||||
pub trait RouteDecoratorExt {
|
||||
|
@ -170,7 +171,7 @@ impl<'a, 'c> RouteDecoratorExt for MetaItemParser<'a, 'c> {
|
|||
|
||||
// Now grab all of the required and optional parameters.
|
||||
let req: [&'static str; 1] = ["path"];
|
||||
let opt: [&'static str; 2] = ["form", "content"];
|
||||
let opt: [&'static str; 3] = ["form", "content", "rank"];
|
||||
let kv_pairs = get_key_values(self.ctxt, self.meta_item.span,
|
||||
&req, &opt, kv_params);
|
||||
|
||||
|
@ -218,11 +219,27 @@ impl<'a, 'c> RouteDecoratorExt for MetaItemParser<'a, 'c> {
|
|||
}
|
||||
}).unwrap_or_else(|| KVSpanned::dummy(ContentType::any()));
|
||||
|
||||
let rank = kv_pairs.get("rank").and_then(|data| {
|
||||
debug!("Found data: {:?}", data);
|
||||
if let Ok(rank) = isize::from_str(data.node) {
|
||||
if rank < 0 {
|
||||
self.ctxt.span_err(data.v_span, "rank must be nonnegative");
|
||||
None
|
||||
} else {
|
||||
Some(data.clone().map(|_| rank))
|
||||
}
|
||||
} else {
|
||||
self.ctxt.span_err(data.v_span, "rank value must be an integer");
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
RouteParams {
|
||||
method: method,
|
||||
path: path,
|
||||
form: form,
|
||||
content_type: content_type,
|
||||
rank: rank
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -208,39 +208,43 @@ pub fn route_decorator(known_method: Option<Spanned<Method>>, ecx: &mut ExtCtxt,
|
|||
let form_stmt = get_form_stmt(ecx, &mut user_params, &form_params);
|
||||
form_stmt.as_ref().map(|s| debug!("Form stmt: {:?}", stmt_to_string(s)));
|
||||
|
||||
// Generate the statements that will attempt to parse the paramaters during
|
||||
// run-time.
|
||||
// Generate the statements that will parse paramaters during run-time.
|
||||
let mut fn_param_exprs = vec![];
|
||||
for (i, param) in user_params.iter().enumerate() {
|
||||
let ident = str_to_ident(param.as_str());
|
||||
let ty = ¶m.ty;
|
||||
let param_fn_item =
|
||||
if param.declared {
|
||||
quote_stmt!(ecx,
|
||||
let $ident: $ty = match _req.get_param($i) {
|
||||
Ok(v) => v,
|
||||
Err(_) => return ::rocket::Response::forward()
|
||||
};
|
||||
).unwrap()
|
||||
} else {
|
||||
quote_stmt!(ecx,
|
||||
let $ident: $ty = match
|
||||
<$ty as ::rocket::request::FromRequest>::from_request(&_req) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
// TODO: Add $ident and $ty to the string.
|
||||
// TODO: Add some kind of loggin facility in Rocket
|
||||
// to get the formatting right (IE, so it idents
|
||||
// correctly).
|
||||
// debug!("Failed to parse: {:?}", e);
|
||||
return ::rocket::Response::forward();
|
||||
}
|
||||
};
|
||||
).unwrap()
|
||||
};
|
||||
|
||||
debug!("Param FN: {}", stmt_to_string(¶m_fn_item));
|
||||
fn_param_exprs.push(param_fn_item);
|
||||
// Push all of the declared parameters.
|
||||
for (i, param) in user_params.iter().filter(|p| p.declared).enumerate() {
|
||||
let (ident, ty) = (str_to_ident(param.as_str()), ¶m.ty);
|
||||
let fn_item = quote_stmt!(ecx,
|
||||
let $ident: $ty = match _req.get_param($i) {
|
||||
Ok(v) => v,
|
||||
Err(_) => return ::rocket::Response::forward()
|
||||
};
|
||||
).unwrap();
|
||||
|
||||
debug!("Declared FN: {}", stmt_to_string(&fn_item));
|
||||
fn_param_exprs.push(fn_item);
|
||||
}
|
||||
|
||||
// Push all of the undeclared (FromRequest) parameters.
|
||||
for param in user_params.iter().filter(|p| !p.declared) {
|
||||
let (ident, ty) = (str_to_ident(param.as_str()), ¶m.ty);
|
||||
let fn_item = quote_stmt!(ecx,
|
||||
let $ident: $ty = match
|
||||
<$ty as ::rocket::request::FromRequest>::from_request(&_req) {
|
||||
Ok(v) => v,
|
||||
Err(_e) => {
|
||||
// TODO: Add $ident and $ty to the string.
|
||||
// TODO: Add some kind of loggin facility in Rocket
|
||||
// to get the formatting right (IE, so it idents
|
||||
// correctly).
|
||||
// debug!("Failed to parse: {:?}", e);
|
||||
return ::rocket::Response::forward();
|
||||
}
|
||||
};
|
||||
).unwrap();
|
||||
|
||||
debug!("Param FN: {}", stmt_to_string(&fn_item));
|
||||
fn_param_exprs.push(fn_item);
|
||||
}
|
||||
|
||||
let route_fn_name = prepend_ident(ROUTE_FN_PREFIX, &item.ident);
|
||||
|
@ -262,6 +266,7 @@ pub fn route_decorator(known_method: Option<Spanned<Method>>, ecx: &mut ExtCtxt,
|
|||
let path = &route.path.node;
|
||||
let method = method_variant_to_expr(ecx, route.method.node);
|
||||
let content_type = content_type_to_expr(ecx, &route.content_type.node);
|
||||
let rank = option_as_expr(ecx, &route.rank);
|
||||
|
||||
let static_item = quote_item!(ecx,
|
||||
#[allow(non_upper_case_globals)]
|
||||
|
@ -271,6 +276,7 @@ pub fn route_decorator(known_method: Option<Spanned<Method>>, ecx: &mut ExtCtxt,
|
|||
path: $path,
|
||||
handler: $route_fn_name,
|
||||
content_type: $content_type,
|
||||
rank: $rank,
|
||||
};
|
||||
).unwrap();
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::ops::Deref;
|
|||
use syntax::parse::{token};
|
||||
use syntax::parse::token::Token;
|
||||
use syntax::tokenstream::TokenTree;
|
||||
use syntax::ast::{Path, Ident, MetaItem, MetaItemKind, LitKind, Ty, self};
|
||||
use syntax::ast::{Path, Expr, Ident, MetaItem, MetaItemKind, LitKind, Ty, self};
|
||||
use syntax::ext::base::{ExtCtxt};
|
||||
use syntax::codemap::{Span, Spanned, BytePos, DUMMY_SP};
|
||||
use syntax::ext::quote::rt::ToTokens;
|
||||
|
@ -227,6 +227,13 @@ pub fn prefix_paths(prefix: &str, paths: &mut Vec<Path>) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn option_as_expr<T: ToTokens>(ecx: &ExtCtxt, opt: &Option<T>) -> P<Expr> {
|
||||
match *opt {
|
||||
Some(ref item) => quote_expr!(ecx, Some($item)),
|
||||
None => quote_expr!(ecx, None)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SimpleArg {
|
||||
pub name: String,
|
||||
|
|
Loading…
Reference in New Issue