mirror of https://github.com/rwf2/Rocket.git
Major overhual: Request, ErrorHandler, ContentType.
This commit is contained in:
parent
2b7b733e83
commit
90d8621adf
|
@ -4,9 +4,7 @@
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
|
|
||||||
use rocket::{Rocket, RoutingError};
|
use rocket::{Rocket, Request, ContentType, Error};
|
||||||
use rocket::ContentType;
|
|
||||||
use rocket::Error;
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
struct Person {
|
struct Person {
|
||||||
|
@ -25,23 +23,23 @@ fn hello(name: String, age: i8) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[error(code = "404")]
|
#[error(code = "404")]
|
||||||
fn not_found(error: RoutingError) -> String {
|
fn not_found<'r>(error: Error, request: &'r Request<'r>) -> String {
|
||||||
match error.error {
|
match error {
|
||||||
// Error::BadMethod if !error.request.content_type.is_json() => {
|
Error::NoRoute if !request.content_type().is_json() => {
|
||||||
// format!("<p>This server only supports JSON requests, not '{}'.</p>",
|
format!("<p>This server only supports JSON requests, not '{}'.</p>",
|
||||||
// error.request.data)
|
request.content_type())
|
||||||
// }
|
}
|
||||||
Error::BadMethod => {
|
Error::NoRoute => {
|
||||||
format!("<p>Sorry, this server but '{}' is not a valid path!</p>
|
format!("<p>Sorry, this server but '{}' is not a valid path!</p>
|
||||||
<p>Try visiting /hello/<name>/<age> instead.</p>",
|
<p>Try visiting /hello/<name>/<age> instead.</p>",
|
||||||
error.request.uri)
|
request.uri())
|
||||||
}
|
}
|
||||||
_ => format!("<p>Bad Request</p>"),
|
_ => format!("<p>Bad Request</p>"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut rocket = Rocket::new("localhost", 8000);
|
let mut rocket = Rocket::new("0.0.0.0", 8000);
|
||||||
rocket.mount("/hello", routes![hello]);
|
rocket.mount("/hello", routes![hello]);
|
||||||
rocket.catch(errors![not_found]);
|
rocket.catch(errors![not_found]);
|
||||||
rocket.launch();
|
rocket.launch();
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#![plugin(rocket_macros)]
|
#![plugin(rocket_macros)]
|
||||||
|
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
use rocket::{Rocket, RoutingError};
|
use rocket::{Rocket, Error, Request};
|
||||||
|
|
||||||
#[route(GET, path = "/hello/<name>/<age>")]
|
#[route(GET, path = "/hello/<name>/<age>")]
|
||||||
fn hello(name: &str, age: i8) -> String {
|
fn hello(name: &str, age: i8) -> String {
|
||||||
|
@ -10,10 +10,10 @@ fn hello(name: &str, age: i8) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[error(code = "404")]
|
#[error(code = "404")]
|
||||||
fn not_found(error: RoutingError) -> String {
|
fn not_found<'r>(_error: Error, request: &'r Request<'r>) -> String {
|
||||||
format!("<p>Sorry, but '{}' is not a valid path!</p>
|
format!("<p>Sorry, but '{}' is not a valid path!</p>
|
||||||
<p>Try visiting /hello/<name>/<age> instead.</p>",
|
<p>Try visiting /hello/<name>/<age> instead.</p>",
|
||||||
error.request.uri)
|
request.uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -3,14 +3,14 @@ extern crate rocket;
|
||||||
use rocket::{Rocket, Request, Response, Route};
|
use rocket::{Rocket, Request, Response, Route};
|
||||||
use rocket::Method::*;
|
use rocket::Method::*;
|
||||||
|
|
||||||
fn root(req: Request) -> Response {
|
fn root<'r>(req: &'r Request<'r>) -> Response<'r> {
|
||||||
let name = req.get_param(0).unwrap_or("unnamed");
|
let name = req.get_param(0).unwrap_or("unnamed");
|
||||||
Response::new(format!("Hello, {}!", name))
|
Response::new(format!("Hello, {}!", name))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn echo_url<'a>(req: Request<'a>) -> Response<'a> {
|
fn echo_url<'a>(req: &'a Request<'a>) -> Response<'a> {
|
||||||
Response::new(req.get_uri().split_at(6).1)
|
Response::new(req.uri().as_str().split_at(6).1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use handler::Handler;
|
use handler::ErrorHandler;
|
||||||
use response::Response;
|
use response::Response;
|
||||||
use error::RoutingError;
|
|
||||||
use codegen::StaticCatchInfo;
|
use codegen::StaticCatchInfo;
|
||||||
|
use error::Error;
|
||||||
|
use request::Request;
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use term_painter::ToStyle;
|
use term_painter::ToStyle;
|
||||||
|
@ -9,7 +10,7 @@ use term_painter::Color::*;
|
||||||
|
|
||||||
pub struct Catcher {
|
pub struct Catcher {
|
||||||
pub code: u16,
|
pub code: u16,
|
||||||
handler: Handler,
|
handler: ErrorHandler,
|
||||||
is_default: bool
|
is_default: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,15 +19,15 @@ pub struct Catcher {
|
||||||
// interface here?
|
// interface here?
|
||||||
|
|
||||||
impl Catcher {
|
impl Catcher {
|
||||||
pub fn new(code: u16, handler: Handler) -> Catcher {
|
pub fn new(code: u16, handler: ErrorHandler) -> Catcher {
|
||||||
Catcher::new_with_default(code, handler, false)
|
Catcher::new_with_default(code, handler, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle<'a>(&'a self, error: RoutingError<'a>) -> Response {
|
pub fn handle<'r>(&self, error: Error, request: &'r Request<'r>) -> Response<'r> {
|
||||||
(self.handler)(error.request)
|
(self.handler)(error, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_with_default(code: u16, handler: Handler, default: bool) -> Catcher {
|
fn new_with_default(code: u16, handler: ErrorHandler, default: bool) -> Catcher {
|
||||||
Catcher {
|
Catcher {
|
||||||
code: code,
|
code: code,
|
||||||
handler: handler,
|
handler: handler,
|
||||||
|
@ -55,9 +56,10 @@ pub mod defaults {
|
||||||
use request::Request;
|
use request::Request;
|
||||||
use response::{StatusCode, Response};
|
use response::{StatusCode, Response};
|
||||||
use super::Catcher;
|
use super::Catcher;
|
||||||
|
use error::Error;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub fn not_found(_request: Request) -> Response {
|
pub fn not_found<'r>(_error: Error, _request: &'r Request<'r>) -> Response<'r> {
|
||||||
Response::with_status(StatusCode::NotFound, "\
|
Response::with_status(StatusCode::NotFound, "\
|
||||||
<head>\
|
<head>\
|
||||||
<meta charset=\"utf-8\">\
|
<meta charset=\"utf-8\">\
|
||||||
|
@ -72,7 +74,8 @@ pub mod defaults {
|
||||||
")
|
")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn internal_error(_request: Request) -> Response {
|
pub fn internal_error<'r>(_error: Error, _request: &'r Request<'r>)
|
||||||
|
-> Response<'r> {
|
||||||
Response::with_status(StatusCode::InternalServerError, "\
|
Response::with_status(StatusCode::InternalServerError, "\
|
||||||
<head>\
|
<head>\
|
||||||
<meta charset=\"utf-8\">\
|
<meta charset=\"utf-8\">\
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ::{Method, Handler};
|
use ::{Method, Handler, ErrorHandler};
|
||||||
use content_type::ContentType;
|
use content_type::ContentType;
|
||||||
|
|
||||||
pub struct StaticRouteInfo {
|
pub struct StaticRouteInfo {
|
||||||
|
@ -10,6 +10,6 @@ pub struct StaticRouteInfo {
|
||||||
|
|
||||||
pub struct StaticCatchInfo {
|
pub struct StaticCatchInfo {
|
||||||
pub code: u16,
|
pub code: u16,
|
||||||
pub handler: Handler
|
pub handler: ErrorHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,23 @@
|
||||||
pub use mime::{Mime, TopLevel, SubLevel};
|
pub use hyper::mime::{Mime, TopLevel, SubLevel};
|
||||||
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use mime::{Param};
|
use std::borrow::Borrow;
|
||||||
|
use std::fmt;
|
||||||
|
use hyper::mime::{Param};
|
||||||
use self::TopLevel::{Text, Application};
|
use self::TopLevel::{Text, Application};
|
||||||
use self::SubLevel::{Json, Html};
|
use self::SubLevel::{Json, Html};
|
||||||
|
|
||||||
|
use router::Collider;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ContentType(pub TopLevel, pub SubLevel, pub Option<Vec<Param>>);
|
pub struct ContentType(pub TopLevel, pub SubLevel, pub Option<Vec<Param>>);
|
||||||
|
|
||||||
impl ContentType {
|
impl ContentType {
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn new(t: TopLevel, s: SubLevel, params: Option<Vec<Param>>) -> ContentType {
|
||||||
|
ContentType(t, s, params)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn of(t: TopLevel, s: SubLevel) -> ContentType {
|
pub fn of(t: TopLevel, s: SubLevel) -> ContentType {
|
||||||
ContentType(t, s, None)
|
ContentType(t, s, None)
|
||||||
|
@ -20,17 +29,11 @@ impl ContentType {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_json(&self) -> bool {
|
pub fn is_json(&self) -> bool {
|
||||||
match *self {
|
self.0 == Application && self.1 == Json
|
||||||
ContentType(Application, Json, _) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_any(&self) -> bool {
|
pub fn is_any(&self) -> bool {
|
||||||
match *self {
|
self.0 == TopLevel::Star && self.1 == SubLevel::Star
|
||||||
ContentType(TopLevel::Star, SubLevel::Star, None) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_ext(&self) -> bool {
|
pub fn is_ext(&self) -> bool {
|
||||||
|
@ -44,10 +47,7 @@ impl ContentType {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_html(&self) -> bool {
|
pub fn is_html(&self) -> bool {
|
||||||
match *self {
|
self.0 == Text && self.1 == Html
|
||||||
ContentType(Text, Html, _) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +57,13 @@ impl Into<Mime> for ContentType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Borrow<Mime>> From<T> for ContentType {
|
||||||
|
default fn from(mime: T) -> ContentType {
|
||||||
|
let mime: Mime = mime.borrow().clone();
|
||||||
|
ContentType::from(mime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Mime> for ContentType {
|
impl From<Mime> for ContentType {
|
||||||
fn from(mime: Mime) -> ContentType {
|
fn from(mime: Mime) -> ContentType {
|
||||||
let params = match mime.2.len() {
|
let params = match mime.2.len() {
|
||||||
|
@ -68,11 +75,153 @@ impl From<Mime> for ContentType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for ContentType {
|
fn is_valid_first_char(c: char) -> bool {
|
||||||
type Err = ();
|
match c {
|
||||||
|
'a'...'z' | 'A'...'Z' | '0'...'9' | '*' => true,
|
||||||
fn from_str(raw: &str) -> Result<ContentType, ()> {
|
_ => false
|
||||||
let mime = Mime::from_str(raw)?;
|
}
|
||||||
Ok(ContentType::from(mime))
|
}
|
||||||
|
|
||||||
|
fn is_valid_char(c: char) -> bool {
|
||||||
|
is_valid_first_char(c) || match c {
|
||||||
|
'!' | '#' | '$' | '&' | '-' | '^' | '.' | '+' | '_' => true,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for ContentType {
|
||||||
|
type Err = &'static str;
|
||||||
|
|
||||||
|
fn from_str(raw: &str) -> Result<ContentType, &'static str> {
|
||||||
|
let slash = match raw.find('/') {
|
||||||
|
Some(i) => i,
|
||||||
|
None => return Err("Missing / in MIME type.")
|
||||||
|
};
|
||||||
|
|
||||||
|
let top_str = &raw[..slash];
|
||||||
|
let (sub_str, rest) = match raw.find(';') {
|
||||||
|
Some(j) => (&raw[(slash + 1)..j], Some(&raw[(j + 1)..])),
|
||||||
|
None => (&raw[(slash + 1)..], None)
|
||||||
|
};
|
||||||
|
|
||||||
|
if top_str.len() < 1 || sub_str.len() < 1 {
|
||||||
|
return Err("Empty string.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !is_valid_first_char(top_str.chars().next().unwrap())
|
||||||
|
|| !is_valid_first_char(sub_str.chars().next().unwrap()) {
|
||||||
|
return Err("Invalid first char.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if top_str.contains(|c| !is_valid_char(c))
|
||||||
|
|| sub_str.contains(|c| !is_valid_char(c)) {
|
||||||
|
return Err("Invalid character in string.")
|
||||||
|
}
|
||||||
|
|
||||||
|
let (top_str, sub_str) = (&*top_str.to_lowercase(), &*sub_str.to_lowercase());
|
||||||
|
let top_level = TopLevel::from_str(top_str).map_err(|_| "Bad TopLevel")?;
|
||||||
|
let sub_level = SubLevel::from_str(sub_str).map_err(|_| "Bad SubLevel")?;
|
||||||
|
// FIXME: Use `rest` to find params.
|
||||||
|
Ok(ContentType::new(top_level, sub_level, None))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ContentType {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}/{}", self.0.as_str(), self.1.as_str())?;
|
||||||
|
|
||||||
|
self.2.as_ref().map_or(Ok(()), |params| {
|
||||||
|
for param in params.iter() {
|
||||||
|
let (ref attr, ref value) = *param;
|
||||||
|
write!(f, "; {}={}", attr, value)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Collider for ContentType {
|
||||||
|
fn collides_with(&self, other: &ContentType) -> bool {
|
||||||
|
self.0.collides_with(&other.0) && self.1.collides_with(&other.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Collider for TopLevel {
|
||||||
|
fn collides_with(&self, other: &TopLevel) -> bool {
|
||||||
|
*self == TopLevel::Star
|
||||||
|
|| *other == TopLevel::Star
|
||||||
|
|| *self == *other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Collider for SubLevel {
|
||||||
|
fn collides_with(&self, other: &SubLevel) -> bool {
|
||||||
|
*self == SubLevel::Star
|
||||||
|
|| *other == SubLevel::Star
|
||||||
|
|| *self == *other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::ContentType;
|
||||||
|
use hyper::mime::{TopLevel, SubLevel};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
|
||||||
|
macro_rules! assert_no_parse {
|
||||||
|
($string:expr) => ({
|
||||||
|
let result = ContentType::from_str($string);
|
||||||
|
if !result.is_err() {
|
||||||
|
println!("{} parsed!", $string);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! assert_parse {
|
||||||
|
($string:expr) => ({
|
||||||
|
let result = ContentType::from_str($string);
|
||||||
|
assert!(result.is_ok());
|
||||||
|
result.unwrap()
|
||||||
|
});
|
||||||
|
($string:expr, $top:tt/$sub:tt) => ({
|
||||||
|
let c = assert_parse!($string);
|
||||||
|
assert_eq!(c.0, TopLevel::$top);
|
||||||
|
assert_eq!(c.1, SubLevel::$sub);
|
||||||
|
c
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_simple() {
|
||||||
|
assert_parse!("application/json", Application/Json);
|
||||||
|
assert_parse!("*/json", Star/Json);
|
||||||
|
assert_parse!("text/html", Text/Html);
|
||||||
|
assert_parse!("TEXT/html", Text/Html);
|
||||||
|
assert_parse!("*/*", Star/Star);
|
||||||
|
assert_parse!("application/*", Application/Star);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_params() {
|
||||||
|
assert_parse!("application/json; charset=utf8", Application/Json);
|
||||||
|
assert_parse!("application/*;charset=utf8;else=1", Application/Star);
|
||||||
|
assert_parse!("*/*;charset=utf8;else=1", Star/Star);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bad_parses() {
|
||||||
|
assert_no_parse!("application//json");
|
||||||
|
assert_no_parse!("application///json");
|
||||||
|
assert_no_parse!("/json");
|
||||||
|
assert_no_parse!("text/");
|
||||||
|
assert_no_parse!("text//");
|
||||||
|
assert_no_parse!("/");
|
||||||
|
assert_no_parse!("*/");
|
||||||
|
assert_no_parse!("/*");
|
||||||
|
assert_no_parse!("///");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,35 +1,7 @@
|
||||||
use request::Request;
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
BadMethod,
|
BadMethod,
|
||||||
BadParse,
|
BadParse,
|
||||||
NoRoute,
|
NoRoute, // FIXME: Add a chain of routes attempted.
|
||||||
NoKey
|
NoKey
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RoutingError<'r> {
|
|
||||||
pub error: Error,
|
|
||||||
pub request: Request<'r>,
|
|
||||||
pub chain: Option<&'r [&'r str]>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RoutingError<'a> {
|
|
||||||
pub fn unchained(request: Request<'a>)
|
|
||||||
-> RoutingError<'a> {
|
|
||||||
RoutingError {
|
|
||||||
error: Error::NoRoute,
|
|
||||||
request: request,
|
|
||||||
chain: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new(error: Error, request: Request<'a>, chain: &'a [&'a str])
|
|
||||||
-> RoutingError<'a> {
|
|
||||||
RoutingError {
|
|
||||||
error: error,
|
|
||||||
request: request,
|
|
||||||
chain: Some(chain)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -23,9 +23,10 @@ mod codegen;
|
||||||
mod catcher;
|
mod catcher;
|
||||||
|
|
||||||
pub mod handler {
|
pub mod handler {
|
||||||
use super::{Request, Response};
|
use super::{Request, Response, Error};
|
||||||
|
|
||||||
pub type Handler = for<'r> fn(Request<'r>) -> Response<'r>;
|
pub type Handler = for<'r> fn(&'r Request<'r>) -> Response<'r>;
|
||||||
|
pub type ErrorHandler = for<'r> fn(error: Error, &'r Request<'r>) -> Response<'r>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use logger::RocketLogger;
|
pub use logger::RocketLogger;
|
||||||
|
@ -34,9 +35,9 @@ pub use codegen::{StaticRouteInfo, StaticCatchInfo};
|
||||||
pub use request::Request;
|
pub use request::Request;
|
||||||
pub use method::Method;
|
pub use method::Method;
|
||||||
pub use response::{Response, Responder};
|
pub use response::{Response, Responder};
|
||||||
pub use error::{Error, RoutingError};
|
pub use error::Error;
|
||||||
pub use param::FromParam;
|
pub use param::FromParam;
|
||||||
pub use router::{Router, Route};
|
pub use router::{Router, Route};
|
||||||
pub use catcher::Catcher;
|
pub use catcher::Catcher;
|
||||||
pub use rocket::Rocket;
|
pub use rocket::Rocket;
|
||||||
pub use handler::Handler;
|
pub use handler::{Handler, ErrorHandler};
|
||||||
|
|
|
@ -75,8 +75,9 @@ impl Log for RocketLogger {
|
||||||
}
|
}
|
||||||
Debug => {
|
Debug => {
|
||||||
let loc = record.location();
|
let loc = record.location();
|
||||||
println!("{} {}:{}", Cyan.paint("-->"), loc.file(), loc.line());
|
print!("\n{} ", Blue.bold().paint("-->"));
|
||||||
println!("{}", Cyan.paint(record.args()));
|
println!("{}:{}", Blue.paint(loc.file()), Blue.paint(loc.line()));
|
||||||
|
println!("{}", record.args());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ impl Method {
|
||||||
HyperMethod::Trace => Some(Trace),
|
HyperMethod::Trace => Some(Trace),
|
||||||
HyperMethod::Connect => Some(Connect),
|
HyperMethod::Connect => Some(Connect),
|
||||||
HyperMethod::Patch => Some(Patch),
|
HyperMethod::Patch => Some(Patch),
|
||||||
_ => None
|
HyperMethod::Extension(_) => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ impl FromStr for Method {
|
||||||
"TRACE" => Ok(Trace),
|
"TRACE" => Ok(Trace),
|
||||||
"CONNECT" => Ok(Connect),
|
"CONNECT" => Ok(Connect),
|
||||||
"PATCH" => Ok(Patch),
|
"PATCH" => Ok(Patch),
|
||||||
_ => Err(Error::BadMethod)
|
_ => Err(Error::BadMethod),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ impl<'r, 'c> FromRequest<'r, 'c> for Cookies {
|
||||||
type Error = &'static str;
|
type Error = &'static str;
|
||||||
|
|
||||||
fn from_request(request: &'r Request<'c>) -> Result<Self, Self::Error> {
|
fn from_request(request: &'r Request<'c>) -> Result<Self, Self::Error> {
|
||||||
match request.headers.get::<HyperCookie>() {
|
match request.headers().get::<HyperCookie>() {
|
||||||
// TODO: What to do about key?
|
// TODO: What to do about key?
|
||||||
Some(cookie) => Ok(cookie.to_cookie_jar(&[])),
|
Some(cookie) => Ok(cookie.to_cookie_jar(&[])),
|
||||||
None => Ok(Cookies::new(&[]))
|
None => Ok(Cookies::new(&[]))
|
||||||
|
|
|
@ -1,38 +1,96 @@
|
||||||
|
use std::io::{Read};
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
use error::Error;
|
use error::Error;
|
||||||
use param::FromParam;
|
use param::FromParam;
|
||||||
use method::Method;
|
use method::Method;
|
||||||
use request::HyperHeaders;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
use content_type::ContentType;
|
||||||
|
use hyper::uri::RequestUri as HyperRequestUri;
|
||||||
|
use hyper::header;
|
||||||
|
use router::URIBuf;
|
||||||
|
use router::URI;
|
||||||
|
use router::Route;
|
||||||
|
|
||||||
|
// Hyper stuff.
|
||||||
|
use request::{HyperHeaders, HyperRequest};
|
||||||
|
|
||||||
pub struct Request<'a> {
|
pub struct Request<'a> {
|
||||||
params: Option<Vec<&'a str>>,
|
pub params: RefCell<Option<Vec<&'a str>>>, // This also sucks.
|
||||||
pub headers: &'a HyperHeaders, // TODO: Don't make pub?....
|
|
||||||
pub method: Method,
|
pub method: Method,
|
||||||
pub uri: &'a str,
|
pub uri: URIBuf, // FIXME: Should be URI (without Hyper).
|
||||||
pub data: &'a [u8]
|
pub data: Vec<u8>, // FIXME: Don't read this! (bad Hyper.)
|
||||||
|
headers: HyperHeaders, // This sucks.
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Request<'a> {
|
impl<'a> Request<'a> {
|
||||||
pub fn new(headers: &'a HyperHeaders, method: Method, uri: &'a str,
|
|
||||||
params: Option<Vec<&'a str>>, data: &'a [u8]) -> Request<'a> {
|
|
||||||
Request {
|
|
||||||
headers: headers,
|
|
||||||
method: method,
|
|
||||||
params: params,
|
|
||||||
uri: uri,
|
|
||||||
data: data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_uri(&self) -> &'a str {
|
|
||||||
self.uri
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_param<T: FromParam<'a>>(&'a self, n: usize) -> Result<T, Error> {
|
pub fn get_param<T: FromParam<'a>>(&'a self, n: usize) -> Result<T, Error> {
|
||||||
if self.params.is_none() || n >= self.params.as_ref().unwrap().len() {
|
let params = self.params.borrow();
|
||||||
|
if params.is_none() || n >= params.as_ref().unwrap().len() {
|
||||||
Err(Error::NoKey)
|
Err(Error::NoKey)
|
||||||
} else {
|
} else {
|
||||||
T::from_param(self.params.as_ref().unwrap()[n])
|
T::from_param(params.as_ref().unwrap()[n])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn mock(method: Method, uri: &str) -> Request {
|
||||||
|
Request {
|
||||||
|
params: RefCell::new(None),
|
||||||
|
method: method,
|
||||||
|
uri: URIBuf::from(uri),
|
||||||
|
data: vec![],
|
||||||
|
headers: HyperHeaders::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// FIXME: Get rid of Hyper.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn headers(&self) -> &HyperHeaders {
|
||||||
|
&self.headers
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uri(&'a self) -> URI<'a> {
|
||||||
|
self.uri.as_uri()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_params(&'a self, route: &Route) {
|
||||||
|
*self.params.borrow_mut() = Some(route.get_params(self.uri.as_uri()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn set_content_type(&mut self, ct: ContentType) {
|
||||||
|
let hyper_ct = header::ContentType(ct.into());
|
||||||
|
self.headers.set::<header::ContentType>(hyper_ct)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'h, 'k> From<HyperRequest<'h, 'k>> for Request<'a> {
|
||||||
|
fn from(hyper_req: HyperRequest<'h, 'k>) -> Request<'a> {
|
||||||
|
let (_, h_method, h_headers, h_uri, _, mut h_body) = hyper_req.deconstruct();
|
||||||
|
|
||||||
|
let uri = match h_uri {
|
||||||
|
HyperRequestUri::AbsolutePath(s) => URIBuf::from(s),
|
||||||
|
_ => panic!("Can only accept absolute paths!")
|
||||||
|
};
|
||||||
|
|
||||||
|
// FIXME: GRRR.
|
||||||
|
let mut data = vec![];
|
||||||
|
h_body.read_to_end(&mut data).unwrap();
|
||||||
|
|
||||||
|
Request {
|
||||||
|
params: RefCell::new(None),
|
||||||
|
method: Method::from_hyp(&h_method).unwrap(),
|
||||||
|
uri: uri,
|
||||||
|
data: data,
|
||||||
|
headers: h_headers,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,20 +28,13 @@ fn uri_is_absolute(uri: &HyperRequestUri) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unwrap_absolute_path(uri: &HyperRequestUri) -> &str {
|
|
||||||
match *uri {
|
|
||||||
HyperRequestUri::AbsolutePath(ref s) => s.as_str(),
|
|
||||||
_ => panic!("Can only accept absolute paths!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn method_is_valid(method: &HyperMethod) -> bool {
|
fn method_is_valid(method: &HyperMethod) -> bool {
|
||||||
Method::from_hyp(method).is_some()
|
Method::from_hyp(method).is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HyperHandler for Rocket {
|
impl HyperHandler for Rocket {
|
||||||
fn handle<'a, 'k>(&'a self, req: HyperRequest<'a, 'k>,
|
fn handle<'h, 'k>(&self, req: HyperRequest<'h, 'k>,
|
||||||
mut res: FreshHyperResponse<'a>) {
|
mut res: FreshHyperResponse<'h>) {
|
||||||
info!("{:?} '{}':", Green.paint(&req.method), Blue.paint(&req.uri));
|
info!("{:?} '{}':", Green.paint(&req.method), Blue.paint(&req.uri));
|
||||||
|
|
||||||
let finalize = |mut req: HyperRequest, _res: FreshHyperResponse| {
|
let finalize = |mut req: HyperRequest, _res: FreshHyperResponse| {
|
||||||
|
@ -68,51 +61,38 @@ impl HyperHandler for Rocket {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rocket {
|
impl Rocket {
|
||||||
fn dispatch<'h, 'k>(&self, mut req: HyperRequest<'h, 'k>,
|
fn dispatch<'h, 'k>(&self, hyper_req: HyperRequest<'h, 'k>,
|
||||||
res: FreshHyperResponse<'h>) {
|
res: FreshHyperResponse<'h>) {
|
||||||
// We read all of the contents now because we have to do it at some
|
let req = Request::from(hyper_req);
|
||||||
// point thanks to Hyper. FIXME: Simple DOS attack here.
|
let route = self.router.route(&req);
|
||||||
let mut buf = vec![];
|
if let Some(route) = route {
|
||||||
let _ = req.read_to_end(&mut buf);
|
// Retrieve and set the requests parameters.
|
||||||
|
req.set_params(&route);
|
||||||
|
|
||||||
// Extract the method, uri, and try to find a route.
|
// Here's the magic: dispatch the request to the handler.
|
||||||
let method = Method::from_hyp(&req.method).unwrap();
|
let outcome = (route.handler)(&req).respond(res);
|
||||||
let uri = unwrap_absolute_path(&req.uri);
|
info_!("{} {}", White.paint("Outcome:"), outcome);
|
||||||
let route = self.router.route(method, uri);
|
|
||||||
|
|
||||||
// A closure which we call when we know there is no route.
|
// // TODO: keep trying lower ranked routes before dispatching a not
|
||||||
let handle_not_found = |response: FreshHyperResponse| {
|
// // found error.
|
||||||
error_!("Dispatch failed. Returning 404.");
|
// outcome.map_forward(|res| {
|
||||||
|
// error_!("No further matching routes.");
|
||||||
let request = Request::new(&req.headers, method, uri, None, &buf);
|
// // TODO: Have some way to know why this was failed forward. Use that
|
||||||
let catcher = self.catchers.get(&404).unwrap();
|
// // instead of always using an unchained error.
|
||||||
catcher.handle(RoutingError::unchained(request)).respond(response);
|
// self.handle_not_found(req, res);
|
||||||
};
|
// });
|
||||||
|
} else {
|
||||||
// No route found. Handle the not_found error and return.
|
|
||||||
if route.is_none() {
|
|
||||||
error_!("No matching routes.");
|
error_!("No matching routes.");
|
||||||
return handle_not_found(res);
|
return self.handle_not_found(&req, res);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Okay, we've got a route. Unwrap it, generate a request, and dispatch.
|
// A closure which we call when we know there is no route.
|
||||||
let route = route.unwrap();
|
fn handle_not_found<'r>(&self, request: &'r Request<'r>,
|
||||||
let params = route.get_params(uri);
|
response: FreshHyperResponse) {
|
||||||
let request = Request::new(&req.headers, method, uri, Some(params), &buf);
|
error_!("Dispatch failed. Returning 404.");
|
||||||
|
let catcher = self.catchers.get(&404).unwrap();
|
||||||
// TODO: Paint these magenta.
|
catcher.handle(Error::NoRoute, request).respond(response);
|
||||||
trace_!("Dispatching request.");
|
|
||||||
let outcome = (route.handler)(request).respond(res);
|
|
||||||
|
|
||||||
// TODO: keep trying lower ranked routes before dispatching a not found
|
|
||||||
// error.
|
|
||||||
info_!("{} {}", White.paint("Outcome:"), outcome);
|
|
||||||
outcome.map_forward(|res| {
|
|
||||||
error_!("No further matching routes.");
|
|
||||||
// TODO: Have some way to know why this was failed forward. Use that
|
|
||||||
// instead of always using an unchained error.
|
|
||||||
handle_not_found(res);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(address: &'static str, port: isize) -> Rocket {
|
pub fn new(address: &'static str, port: isize) -> Rocket {
|
||||||
|
|
|
@ -45,10 +45,12 @@ mod tests {
|
||||||
use Method;
|
use Method;
|
||||||
use Method::*;
|
use Method::*;
|
||||||
use {Request, Response};
|
use {Request, Response};
|
||||||
|
use content_type::{ContentType, TopLevel, SubLevel};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
type SimpleRoute = (Method, &'static str);
|
type SimpleRoute = (Method, &'static str);
|
||||||
|
|
||||||
fn dummy_handler(_req: Request) -> Response<'static> {
|
fn dummy_handler(_req: &Request) -> Response<'static> {
|
||||||
Response::empty()
|
Response::empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,4 +185,52 @@ mod tests {
|
||||||
assert!(!s_s_collide("/a/<b>", "/b/<b>"));
|
assert!(!s_s_collide("/a/<b>", "/b/<b>"));
|
||||||
assert!(!s_s_collide("/a<a>/<b>", "/b/<b>"));
|
assert!(!s_s_collide("/a<a>/<b>", "/b/<b>"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ct_route(m: Method, s: &str, ct: &str) -> Route {
|
||||||
|
let mut route_a = Route::new(m, s, dummy_handler);
|
||||||
|
route_a.content_type = ContentType::from_str(ct).expect("Whoops!");
|
||||||
|
route_a
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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!(!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"));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn r_ct_ct_collide(m1: Method, ct1: &str, m2: Method, ct2: &str) -> bool {
|
||||||
|
let a_route = ct_route(m1, "a", ct1);
|
||||||
|
let b_route = ct_route(m2, "a", ct2);
|
||||||
|
a_route.collides_with(&b_route)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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, "text/html", Get, "application/*"));
|
||||||
|
assert!(!r_ct_ct_collide(Get, "application/html", Get, "text/*"));
|
||||||
|
assert!(!r_ct_ct_collide(Get, "*/json", Get, "text/html"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ pub use self::route::Route;
|
||||||
|
|
||||||
use std::collections::hash_map::HashMap;
|
use std::collections::hash_map::HashMap;
|
||||||
use method::Method;
|
use method::Method;
|
||||||
|
use request::Request;
|
||||||
|
|
||||||
type Selector = (Method, usize);
|
type Selector = (Method, usize);
|
||||||
|
|
||||||
|
@ -31,13 +32,15 @@ impl Router {
|
||||||
// `Route` structure is inflexible. Have it be an associated type.
|
// `Route` structure is inflexible. Have it be an associated type.
|
||||||
// FIXME: Figure out a way to get more than one route, i.e., to correctly
|
// FIXME: Figure out a way to get more than one route, i.e., to correctly
|
||||||
// handle ranking.
|
// handle ranking.
|
||||||
pub fn route<'b>(&'b self, method: Method, uri: &str) -> Option<&'b Route> {
|
// TODO: Should the Selector include the content-type? If it does, can't
|
||||||
let mut matched_route: Option<&Route> = None;
|
// warn the user that a match was found for the wrong content-type. It
|
||||||
|
// doesn't, can, but this method is slower.
|
||||||
|
pub fn route<'b>(&'b self, req: &Request) -> Option<&'b Route> {
|
||||||
|
let num_segments = req.uri.segment_count();
|
||||||
|
|
||||||
let path = URI::new(uri);
|
let mut matched_route: Option<&Route> = None;
|
||||||
let num_segments = path.segment_count();
|
if let Some(routes) = self.routes.get(&(req.method, num_segments)) {
|
||||||
if let Some(routes) = self.routes.get(&(method, num_segments)) {
|
for route in routes.iter().filter(|r| r.collides_with(req)) {
|
||||||
for route in routes.iter().filter(|r| r.collides_with(uri)) {
|
|
||||||
info_!("Matched: {}", route);
|
info_!("Matched: {}", route);
|
||||||
if let Some(existing_route) = matched_route {
|
if let Some(existing_route) = matched_route {
|
||||||
if route.rank > existing_route.rank {
|
if route.rank > existing_route.rank {
|
||||||
|
@ -71,11 +74,13 @@ impl Router {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use Method::*;
|
use method::Method;
|
||||||
|
use method::Method::*;
|
||||||
use super::{Router, Route};
|
use super::{Router, Route};
|
||||||
use {Response, Request};
|
use {Response, Request};
|
||||||
|
use super::URI;
|
||||||
|
|
||||||
fn dummy_handler(_req: Request) -> Response<'static> {
|
fn dummy_handler(_req: &Request) -> Response<'static> {
|
||||||
Response::empty()
|
Response::empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,65 +137,70 @@ mod test {
|
||||||
assert!(!router.has_collisions());
|
assert!(!router.has_collisions());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn route<'a>(router: &'a Router, method: Method, uri: &str) -> Option<&'a Route> {
|
||||||
|
let request = Request::mock(method, uri);
|
||||||
|
router.route(&request)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ok_routing() {
|
fn test_ok_routing() {
|
||||||
let router = router_with_routes(&["/hello"]);
|
let router = router_with_routes(&["/hello"]);
|
||||||
assert!(router.route(Get, "/hello").is_some());
|
assert!(route(&router, Get, "/hello").is_some());
|
||||||
|
|
||||||
let router = router_with_routes(&["/<a>"]);
|
let router = router_with_routes(&["/<a>"]);
|
||||||
assert!(router.route(Get, "/hello").is_some());
|
assert!(route(&router, Get, "/hello").is_some());
|
||||||
assert!(router.route(Get, "/hi").is_some());
|
assert!(route(&router, Get, "/hi").is_some());
|
||||||
assert!(router.route(Get, "/bobbbbbbbbbby").is_some());
|
assert!(route(&router, Get, "/bobbbbbbbbbby").is_some());
|
||||||
assert!(router.route(Get, "/dsfhjasdf").is_some());
|
assert!(route(&router, Get, "/dsfhjasdf").is_some());
|
||||||
|
|
||||||
let router = router_with_routes(&["/<a>/<b>"]);
|
let router = router_with_routes(&["/<a>/<b>"]);
|
||||||
assert!(router.route(Get, "/hello/hi").is_some());
|
assert!(route(&router, Get, "/hello/hi").is_some());
|
||||||
assert!(router.route(Get, "/a/b/").is_some());
|
assert!(route(&router, Get, "/a/b/").is_some());
|
||||||
assert!(router.route(Get, "/i/a").is_some());
|
assert!(route(&router, Get, "/i/a").is_some());
|
||||||
assert!(router.route(Get, "/jdlk/asdij").is_some());
|
assert!(route(&router, Get, "/jdlk/asdij").is_some());
|
||||||
|
|
||||||
let mut router = Router::new();
|
let mut router = Router::new();
|
||||||
router.add(Route::new(Put, "/hello".to_string(), dummy_handler));
|
router.add(Route::new(Put, "/hello".to_string(), dummy_handler));
|
||||||
router.add(Route::new(Post, "/hello".to_string(), dummy_handler));
|
router.add(Route::new(Post, "/hello".to_string(), dummy_handler));
|
||||||
router.add(Route::new(Delete, "/hello".to_string(), dummy_handler));
|
router.add(Route::new(Delete, "/hello".to_string(), dummy_handler));
|
||||||
assert!(router.route(Put, "/hello").is_some());
|
assert!(route(&router, Put, "/hello").is_some());
|
||||||
assert!(router.route(Post, "/hello").is_some());
|
assert!(route(&router, Post, "/hello").is_some());
|
||||||
assert!(router.route(Delete, "/hello").is_some());
|
assert!(route(&router, Delete, "/hello").is_some());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_err_routing() {
|
fn test_err_routing() {
|
||||||
let router = router_with_routes(&["/hello"]);
|
let router = router_with_routes(&["/hello"]);
|
||||||
assert!(router.route(Put, "/hello").is_none());
|
assert!(route(&router, Put, "/hello").is_none());
|
||||||
assert!(router.route(Post, "/hello").is_none());
|
assert!(route(&router, Post, "/hello").is_none());
|
||||||
assert!(router.route(Options, "/hello").is_none());
|
assert!(route(&router, Options, "/hello").is_none());
|
||||||
assert!(router.route(Get, "/hell").is_none());
|
assert!(route(&router, Get, "/hell").is_none());
|
||||||
assert!(router.route(Get, "/hi").is_none());
|
assert!(route(&router, Get, "/hi").is_none());
|
||||||
assert!(router.route(Get, "/hello/there").is_none());
|
assert!(route(&router, Get, "/hello/there").is_none());
|
||||||
assert!(router.route(Get, "/hello/i").is_none());
|
assert!(route(&router, Get, "/hello/i").is_none());
|
||||||
assert!(router.route(Get, "/hillo").is_none());
|
assert!(route(&router, Get, "/hillo").is_none());
|
||||||
|
|
||||||
let router = router_with_routes(&["/<a>"]);
|
let router = router_with_routes(&["/<a>"]);
|
||||||
assert!(router.route(Put, "/hello").is_none());
|
assert!(route(&router, Put, "/hello").is_none());
|
||||||
assert!(router.route(Post, "/hello").is_none());
|
assert!(route(&router, Post, "/hello").is_none());
|
||||||
assert!(router.route(Options, "/hello").is_none());
|
assert!(route(&router, Options, "/hello").is_none());
|
||||||
assert!(router.route(Get, "/hello/there").is_none());
|
assert!(route(&router, Get, "/hello/there").is_none());
|
||||||
assert!(router.route(Get, "/hello/i").is_none());
|
assert!(route(&router, Get, "/hello/i").is_none());
|
||||||
|
|
||||||
let router = router_with_routes(&["/<a>/<b>"]);
|
let router = router_with_routes(&["/<a>/<b>"]);
|
||||||
assert!(router.route(Get, "/a/b/c").is_none());
|
assert!(route(&router, Get, "/a/b/c").is_none());
|
||||||
assert!(router.route(Get, "/a").is_none());
|
assert!(route(&router, Get, "/a").is_none());
|
||||||
assert!(router.route(Get, "/a/").is_none());
|
assert!(route(&router, Get, "/a/").is_none());
|
||||||
assert!(router.route(Get, "/a/b/c/d").is_none());
|
assert!(route(&router, Get, "/a/b/c/d").is_none());
|
||||||
assert!(router.route(Put, "/hello/hi").is_none());
|
assert!(route(&router, Put, "/hello/hi").is_none());
|
||||||
assert!(router.route(Put, "/a/b").is_none());
|
assert!(route(&router, Put, "/a/b").is_none());
|
||||||
assert!(router.route(Put, "/a/b").is_none());
|
assert!(route(&router, Put, "/a/b").is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! assert_ranked_routes {
|
macro_rules! assert_ranked_routes {
|
||||||
($routes:expr, $to:expr, $want:expr) => ({
|
($routes:expr, $to:expr, $want:expr) => ({
|
||||||
let router = router_with_routes($routes);
|
let router = router_with_routes($routes);
|
||||||
let route_path = router.route(Get, $to).unwrap().path.as_str();
|
let route_path = route(&router, Get, $to).unwrap().path.as_str();
|
||||||
assert_eq!(route_path as &str, $want as &str);
|
assert_eq!(route_path as &str, $want as &str);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -211,8 +221,8 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_params(router: &Router, path: &str, expected: &[&str]) -> bool {
|
fn match_params(router: &Router, path: &str, expected: &[&str]) -> bool {
|
||||||
router.route(Get, path).map_or(false, |route| {
|
route(router, Get, path).map_or(false, |route| {
|
||||||
let params = route.get_params(path);
|
let params = route.get_params(URI::new(path));
|
||||||
if params.len() != expected.len() {
|
if params.len() != expected.len() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ use term_painter::Color::*;
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::convert::From;
|
use std::convert::From;
|
||||||
|
use request::Request;
|
||||||
|
|
||||||
pub struct Route {
|
pub struct Route {
|
||||||
pub method: Method,
|
pub method: Method,
|
||||||
|
@ -17,17 +18,6 @@ pub struct Route {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Route {
|
impl Route {
|
||||||
pub fn full<S>(rank: isize, m: Method, path: S, handler: Handler, t: ContentType)
|
|
||||||
-> Route where S: AsRef<str> {
|
|
||||||
Route {
|
|
||||||
method: m,
|
|
||||||
path: URIBuf::from(path.as_ref()),
|
|
||||||
handler: handler,
|
|
||||||
rank: rank,
|
|
||||||
content_type: t,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ranked<S>(rank: isize, m: Method, path: S, handler: Handler)
|
pub fn ranked<S>(rank: isize, m: Method, path: S, handler: Handler)
|
||||||
-> Route where S: AsRef<str> {
|
-> Route where S: AsRef<str> {
|
||||||
Route {
|
Route {
|
||||||
|
@ -39,8 +29,7 @@ impl Route {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new<S>(m: Method, path: S, handler: Handler)
|
pub fn new<S>(m: Method, path: S, handler: Handler) -> Route where S: AsRef<str> {
|
||||||
-> Route where S: AsRef<str> {
|
|
||||||
Route {
|
Route {
|
||||||
method: m,
|
method: m,
|
||||||
handler: handler,
|
handler: handler,
|
||||||
|
@ -57,9 +46,9 @@ impl Route {
|
||||||
// FIXME: Decide whether a component has to be fully variable or not. That
|
// FIXME: Decide whether a component has to be fully variable or not. That
|
||||||
// is, whether you can have: /a<a>b/ or even /<a>:<b>/
|
// is, whether you can have: /a<a>b/ or even /<a>:<b>/
|
||||||
// TODO: Don't return a Vec...take in an &mut [&'a str] (no alloc!)
|
// TODO: Don't return a Vec...take in an &mut [&'a str] (no alloc!)
|
||||||
pub fn get_params<'a>(&self, uri: &'a str) -> Vec<&'a str> {
|
pub fn get_params<'a>(&self, uri: URI<'a>) -> Vec<&'a str> {
|
||||||
let route_components = self.path.segments();
|
let route_components = self.path.segments();
|
||||||
let uri_components = URI::new(uri).segments();
|
let uri_components = uri.segments();
|
||||||
|
|
||||||
let mut result = Vec::with_capacity(self.path.segment_count());
|
let mut result = Vec::with_capacity(self.path.segment_count());
|
||||||
for (route_seg, uri_seg) in route_components.zip(uri_components) {
|
for (route_seg, uri_seg) in route_components.zip(uri_components) {
|
||||||
|
@ -74,25 +63,31 @@ impl Route {
|
||||||
|
|
||||||
impl fmt::Display for Route {
|
impl fmt::Display for Route {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "{} {}", Green.paint(&self.method), Blue.paint(&self.path))
|
write!(f, "{} {} ", Green.paint(&self.method), Blue.paint(&self.path))?;
|
||||||
|
|
||||||
|
if !self.content_type.is_any() {
|
||||||
|
write!(f, "{}", Yellow.paint(&self.content_type))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a StaticRouteInfo> for Route {
|
impl<'a> From<&'a StaticRouteInfo> for Route {
|
||||||
fn from(info: &'a StaticRouteInfo) -> Route {
|
fn from(info: &'a StaticRouteInfo) -> Route {
|
||||||
Route::new(info.method, info.path, info.handler)
|
let mut route = Route::new(info.method, info.path, info.handler);
|
||||||
|
route.content_type = info.content_type.clone();
|
||||||
|
route
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Collider for Route {
|
impl Collider for Route {
|
||||||
fn collides_with(&self, b: &Route) -> bool {
|
fn collides_with(&self, b: &Route) -> bool {
|
||||||
if self.path.segment_count() != b.path.segment_count()
|
self.path.segment_count() == b.path.segment_count()
|
||||||
|| self.method != b.method
|
&& self.method == b.method
|
||||||
|| self.rank != b.rank {
|
&& self.rank == b.rank
|
||||||
return false;
|
&& self.content_type.collides_with(&b.content_type)
|
||||||
}
|
&& self.path.collides_with(&b.path)
|
||||||
|
|
||||||
self.path.collides_with(&b.path)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,3 +103,11 @@ impl Collider<str> for Route {
|
||||||
other.collides_with(self)
|
other.collides_with(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'r> Collider<Request<'r>> for Route {
|
||||||
|
fn collides_with(&self, req: &Request) -> bool {
|
||||||
|
self.method == req.method
|
||||||
|
&& req.uri.collides_with(&self.path)
|
||||||
|
&& req.content_type().collides_with(&self.content_type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -8,3 +8,5 @@ plugin = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rocket = { path = "../lib/" }
|
rocket = { path = "../lib/" }
|
||||||
|
log = "*"
|
||||||
|
env_logger = "*"
|
||||||
|
|
|
@ -7,9 +7,6 @@ use syntax::ast::{MetaItem};
|
||||||
use syntax::ext::base::{Annotatable, ExtCtxt};
|
use syntax::ext::base::{Annotatable, ExtCtxt};
|
||||||
use syntax::print::pprust::{item_to_string};
|
use syntax::print::pprust::{item_to_string};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
const DEBUG: bool = true;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Params {
|
struct Params {
|
||||||
code: KVSpanned<u16>,
|
code: KVSpanned<u16>,
|
||||||
|
@ -58,10 +55,11 @@ pub fn error_decorator(ecx: &mut ExtCtxt, sp: Span, meta_item: &MetaItem,
|
||||||
let catch_fn_name = prepend_ident(CATCH_FN_PREFIX, &item.ident);
|
let catch_fn_name = prepend_ident(CATCH_FN_PREFIX, &item.ident);
|
||||||
let catch_code = error_params.code.node;
|
let catch_code = error_params.code.node;
|
||||||
let catch_fn_item = quote_item!(ecx,
|
let catch_fn_item = quote_item!(ecx,
|
||||||
fn $catch_fn_name<'rocket>(_req: rocket::Request<'rocket>)
|
fn $catch_fn_name<'rocket>(err: ::rocket::Error,
|
||||||
-> rocket::Response<'rocket> {
|
req: &'rocket ::rocket::Request<'rocket>)
|
||||||
|
-> ::rocket::Response<'rocket> {
|
||||||
// TODO: Figure out what type signature of catcher should be.
|
// TODO: Figure out what type signature of catcher should be.
|
||||||
let result = $fn_name(RoutingError::unchained(_req));
|
let result = $fn_name(err, req);
|
||||||
rocket::Response::with_raw_status($catch_code, result)
|
rocket::Response::with_raw_status($catch_code, result)
|
||||||
}
|
}
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
|
@ -7,6 +7,7 @@ extern crate syntax_ext;
|
||||||
extern crate rustc;
|
extern crate rustc;
|
||||||
extern crate rustc_plugin;
|
extern crate rustc_plugin;
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
|
extern crate env_logger;
|
||||||
|
|
||||||
#[macro_use] mod utils;
|
#[macro_use] mod utils;
|
||||||
mod routes_macro;
|
mod routes_macro;
|
||||||
|
|
|
@ -8,9 +8,6 @@ use syntax::ptr::P;
|
||||||
use utils::*;
|
use utils::*;
|
||||||
use rocket::{Method, ContentType};
|
use rocket::{Method, ContentType};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
const DEBUG: bool = true;
|
|
||||||
|
|
||||||
pub struct MetaItemParser<'a, 'c: 'a> {
|
pub struct MetaItemParser<'a, 'c: 'a> {
|
||||||
attr_name: &'a str,
|
attr_name: &'a str,
|
||||||
ctxt: &'a ExtCtxt<'c>,
|
ctxt: &'a ExtCtxt<'c>,
|
||||||
|
@ -122,7 +119,7 @@ pub struct RouteParams {
|
||||||
pub method: Spanned<Method>,
|
pub method: Spanned<Method>,
|
||||||
pub path: KVSpanned<String>,
|
pub path: KVSpanned<String>,
|
||||||
pub form: Option<KVSpanned<String>>,
|
pub form: Option<KVSpanned<String>>,
|
||||||
pub content_type: Option<KVSpanned<ContentType>>,
|
pub content_type: KVSpanned<ContentType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait RouteDecoratorExt {
|
pub trait RouteDecoratorExt {
|
||||||
|
@ -219,8 +216,7 @@ impl<'a, 'c> RouteDecoratorExt for MetaItemParser<'a, 'c> {
|
||||||
self.ctxt.span_err(data.v_span, &msg);
|
self.ctxt.span_err(data.v_span, &msg);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
});
|
}).unwrap_or(KVSpanned::dummy(ContentType::any()));
|
||||||
|
|
||||||
|
|
||||||
RouteParams {
|
RouteParams {
|
||||||
method: method,
|
method: method,
|
||||||
|
|
|
@ -11,7 +11,8 @@ use syntax::ptr::P;
|
||||||
use syntax::print::pprust::{item_to_string, stmt_to_string};
|
use syntax::print::pprust::{item_to_string, stmt_to_string};
|
||||||
use syntax::parse::token::{self, str_to_ident};
|
use syntax::parse::token::{self, str_to_ident};
|
||||||
|
|
||||||
use rocket::Method;
|
use rocket::{Method, ContentType};
|
||||||
|
use rocket::content_type::{TopLevel, SubLevel};
|
||||||
|
|
||||||
pub fn extract_params_from_kv<'a>(parser: &MetaItemParser,
|
pub fn extract_params_from_kv<'a>(parser: &MetaItemParser,
|
||||||
params: &'a KVSpanned<String>) -> Vec<Spanned<&'a str>> {
|
params: &'a KVSpanned<String>) -> Vec<Spanned<&'a str>> {
|
||||||
|
@ -93,11 +94,12 @@ fn get_form_stmt(ecx: &ExtCtxt, fn_args: &mut Vec<UserParam>,
|
||||||
// The actual code we'll be inserting.
|
// The actual code we'll be inserting.
|
||||||
quote_stmt!(ecx,
|
quote_stmt!(ecx,
|
||||||
let $param_ident: $param_ty =
|
let $param_ident: $param_ty =
|
||||||
if let Ok(form_string) = ::std::str::from_utf8(_req.data) {
|
if let Ok(form_string) = ::std::str::from_utf8(_req.data.as_slice()) {
|
||||||
match ::rocket::form::FromForm::from_form_string(form_string) {
|
match ::rocket::form::FromForm::from_form_string(form_string) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
debug!("\t=> Form failed to parse.");
|
// TODO:
|
||||||
|
// debug!("\t=> Form failed to parse.");
|
||||||
return ::rocket::Response::not_found();
|
return ::rocket::Response::not_found();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,9 +109,9 @@ fn get_form_stmt(ecx: &ExtCtxt, fn_args: &mut Vec<UserParam>,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is there a better way to do this? I need something with ToTokens for the
|
// TODO: Is there a better way to do this? I need something with ToTokens for
|
||||||
// quote_expr macro that builds the route struct. I tried using
|
// the quote_expr macro that builds the route struct. I tried using
|
||||||
// str_to_ident("rocket::Method::Options"), but this seems to miss the context,
|
// str_to_ident("::rocket::Method::Options"), but this seems to miss the context,
|
||||||
// and you get an 'ident not found' on compile. I also tried using the path expr
|
// and you get an 'ident not found' on compile. I also tried using the path expr
|
||||||
// builder from ASTBuilder: same thing.
|
// builder from ASTBuilder: same thing.
|
||||||
fn method_variant_to_expr(ecx: &ExtCtxt, method: Method) -> P<Expr> {
|
fn method_variant_to_expr(ecx: &ExtCtxt, method: Method) -> P<Expr> {
|
||||||
|
@ -126,10 +128,61 @@ fn method_variant_to_expr(ecx: &ExtCtxt, method: Method) -> P<Expr> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Same here.
|
||||||
|
fn top_level_to_expr(ecx: &ExtCtxt, level: &TopLevel) -> P<Expr> {
|
||||||
|
use rocket::content_type::TopLevel::*;
|
||||||
|
match *level {
|
||||||
|
Star => quote_expr!(ecx, ::rocket::content_type::TopLevel::Star),
|
||||||
|
Text => quote_expr!(ecx, ::rocket::content_type::TopLevel::Text),
|
||||||
|
Image => quote_expr!(ecx, ::rocket::content_type::TopLevel::Image),
|
||||||
|
Audio => quote_expr!(ecx, ::rocket::content_type::TopLevel::Audio),
|
||||||
|
Video => quote_expr!(ecx, ::rocket::content_type::TopLevel::Video),
|
||||||
|
Application => quote_expr!(ecx, ::rocket::content_type::TopLevel::Application),
|
||||||
|
Multipart => quote_expr!(ecx, ::rocket::content_type::TopLevel::Multipart),
|
||||||
|
Message => quote_expr!(ecx, ::rocket::content_type::TopLevel::Message),
|
||||||
|
Model => quote_expr!(ecx, ::rocket::content_type::TopLevel::Model),
|
||||||
|
Ext(ref s) => quote_expr!(ecx, ::rocket::content_type::TopLevel::Ext($s)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same here.
|
||||||
|
fn sub_level_to_expr(ecx: &ExtCtxt, level: &SubLevel) -> P<Expr> {
|
||||||
|
use rocket::content_type::SubLevel::*;
|
||||||
|
match *level {
|
||||||
|
Star => quote_expr!(ecx, ::rocket::content_type::SubLevel::Star),
|
||||||
|
Plain => quote_expr!(ecx, ::rocket::content_type::SubLevel::Plain),
|
||||||
|
Html => quote_expr!(ecx, ::rocket::content_type::SubLevel::Html),
|
||||||
|
Xml => quote_expr!(ecx, ::rocket::content_type::SubLevel::Xml),
|
||||||
|
Javascript => quote_expr!(ecx, ::rocket::content_type::SubLevel::Javascript),
|
||||||
|
Css => quote_expr!(ecx, ::rocket::content_type::SubLevel::Css),
|
||||||
|
EventStream => quote_expr!(ecx, ::rocket::content_type::SubLevel::EventStream),
|
||||||
|
Json => quote_expr!(ecx, ::rocket::content_type::SubLevel::Json),
|
||||||
|
WwwFormUrlEncoded =>
|
||||||
|
quote_expr!(ecx, ::rocket::content_type::SubLevel::WwwFormUrlEncoded),
|
||||||
|
Msgpack => quote_expr!(ecx, ::rocket::content_type::SubLevel::Msgpack),
|
||||||
|
OctetStream =>
|
||||||
|
quote_expr!(ecx, ::rocket::content_type::SubLevel::OctetStream),
|
||||||
|
FormData => quote_expr!(ecx, ::rocket::content_type::SubLevel::FormData),
|
||||||
|
Png => quote_expr!(ecx, ::rocket::content_type::SubLevel::Png),
|
||||||
|
Gif => quote_expr!(ecx, ::rocket::content_type::SubLevel::Gif),
|
||||||
|
Bmp => quote_expr!(ecx, ::rocket::content_type::SubLevel::Bmp),
|
||||||
|
Jpeg => quote_expr!(ecx, ::rocket::content_type::SubLevel::Jpeg),
|
||||||
|
Ext(ref s) => quote_expr!(ecx, ::rocket::content_type::SubLevel::Ext($s)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn content_type_to_expr(ecx: &ExtCtxt, content_type: &ContentType) -> P<Expr> {
|
||||||
|
let top_level = top_level_to_expr(ecx, &content_type.0);
|
||||||
|
let sub_level = sub_level_to_expr(ecx, &content_type.1);
|
||||||
|
quote_expr!(ecx, ::rocket::ContentType($top_level, $sub_level, None))
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: Compilation fails when parameters have the same name as the function!
|
// FIXME: Compilation fails when parameters have the same name as the function!
|
||||||
pub fn route_decorator(known_method: Option<Spanned<Method>>, ecx: &mut ExtCtxt,
|
pub fn route_decorator(known_method: Option<Spanned<Method>>, ecx: &mut ExtCtxt,
|
||||||
sp: Span, meta_item: &MetaItem, annotated: &Annotatable,
|
sp: Span, meta_item: &MetaItem, annotated: &Annotatable,
|
||||||
push: &mut FnMut(Annotatable)) {
|
push: &mut FnMut(Annotatable)) {
|
||||||
|
::rocket::logger::init(::rocket::logger::Level::Debug);
|
||||||
|
|
||||||
// Get the encompassing item and function declaration for the annotated func.
|
// Get the encompassing item and function declaration for the annotated func.
|
||||||
let parser = MetaItemParser::new(ecx, meta_item, annotated, &sp);
|
let parser = MetaItemParser::new(ecx, meta_item, annotated, &sp);
|
||||||
let (item, fn_decl) = (parser.expect_item(), parser.expect_fn_decl());
|
let (item, fn_decl) = (parser.expect_item(), parser.expect_fn_decl());
|
||||||
|
@ -186,14 +239,14 @@ pub fn route_decorator(known_method: Option<Spanned<Method>>, ecx: &mut ExtCtxt,
|
||||||
).unwrap()
|
).unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("Param FN: {:?}", stmt_to_string(¶m_fn_item));
|
debug!("Param FN: {}", stmt_to_string(¶m_fn_item));
|
||||||
fn_param_exprs.push(param_fn_item);
|
fn_param_exprs.push(param_fn_item);
|
||||||
}
|
}
|
||||||
|
|
||||||
let route_fn_name = prepend_ident(ROUTE_FN_PREFIX, &item.ident);
|
let route_fn_name = prepend_ident(ROUTE_FN_PREFIX, &item.ident);
|
||||||
let fn_name = item.ident;
|
let fn_name = item.ident;
|
||||||
let route_fn_item = quote_item!(ecx,
|
let route_fn_item = quote_item!(ecx,
|
||||||
fn $route_fn_name<'rocket>(_req: ::rocket::Request<'rocket>)
|
fn $route_fn_name<'rocket>(_req: &'rocket ::rocket::Request<'rocket>)
|
||||||
-> ::rocket::Response<'rocket> {
|
-> ::rocket::Response<'rocket> {
|
||||||
$form_stmt
|
$form_stmt
|
||||||
$fn_param_exprs
|
$fn_param_exprs
|
||||||
|
@ -208,19 +261,21 @@ pub fn route_decorator(known_method: Option<Spanned<Method>>, ecx: &mut ExtCtxt,
|
||||||
let struct_name = prepend_ident(ROUTE_STRUCT_PREFIX, &item.ident);
|
let struct_name = prepend_ident(ROUTE_STRUCT_PREFIX, &item.ident);
|
||||||
let path = &route.path.node;
|
let path = &route.path.node;
|
||||||
let method = method_variant_to_expr(ecx, route.method.node);
|
let method = method_variant_to_expr(ecx, route.method.node);
|
||||||
push(Annotatable::Item(quote_item!(ecx,
|
let content_type = content_type_to_expr(ecx, &route.content_type.node);
|
||||||
|
|
||||||
|
let static_item = quote_item!(ecx,
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
pub static $struct_name: ::rocket::StaticRouteInfo =
|
pub static $struct_name: ::rocket::StaticRouteInfo =
|
||||||
::rocket::StaticRouteInfo {
|
::rocket::StaticRouteInfo {
|
||||||
method: $method,
|
method: $method,
|
||||||
path: $path,
|
path: $path,
|
||||||
handler: $route_fn_name,
|
handler: $route_fn_name,
|
||||||
content_type: ::rocket::ContentType(
|
content_type: $content_type,
|
||||||
::rocket::content_type::TopLevel::Star,
|
|
||||||
::rocket::content_type::SubLevel::Star,
|
|
||||||
None)
|
|
||||||
};
|
};
|
||||||
).unwrap()));
|
).unwrap();
|
||||||
|
|
||||||
|
debug!("Emitting static: {}", item_to_string(&static_item));
|
||||||
|
push(Annotatable::Item(static_item));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue