mirror of https://github.com/rwf2/Rocket.git
Major changes. FN params are now being used! Woo!
Subset of list of changes: * Split up decorator and macro into their own files. * Fully parsing the path parameter and verifying against the function's args. * Actually calling methods to fetch and convert the request parameters. * Actually calling methods to convert the handler's return type. * Sketched out more of the Request/Response structures. Pretty close to having a fully working MVP.
This commit is contained in:
parent
d5db70afc4
commit
ad08fe1d04
|
@ -2,24 +2,19 @@
|
|||
#![plugin(rocket_macros)]
|
||||
|
||||
extern crate rocket;
|
||||
use rocket::{Rocket, Request, Response, Method, Route};
|
||||
use rocket::Rocket;
|
||||
|
||||
#[route(GET, path = "/hello")]
|
||||
fn hello() -> &'static str {
|
||||
"Hello, world!"
|
||||
#[route(GET, path = "/<name>")]
|
||||
fn hello(name: String) -> String {
|
||||
format!("Hello, {}!", name)
|
||||
}
|
||||
|
||||
mod test {
|
||||
use rocket::{Request, Response, Method, Route};
|
||||
|
||||
#[route(GET, path = "")]
|
||||
pub fn hello() -> &'static str {
|
||||
"Hello, world!"
|
||||
}
|
||||
#[route(PUT, path = "/<x>/<y>")]
|
||||
fn bye(x: usize, y: usize) -> String {
|
||||
format!("{} + {} = {}", x, y, x + y)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut rocket = Rocket::new("localhost", 8000);
|
||||
rocket.mount("/test", routes![test::hello]);
|
||||
rocket.mount_and_launch("/", routes![hello]);
|
||||
let rocket = Rocket::new("localhost", 8000);
|
||||
rocket.mount_and_launch("/", routes![hello, bye]);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
pub enum Error {
|
||||
BadMethod
|
||||
BadMethod,
|
||||
BadParse,
|
||||
NoKey
|
||||
}
|
||||
|
|
|
@ -2,9 +2,13 @@ extern crate hyper;
|
|||
|
||||
mod method;
|
||||
mod error;
|
||||
mod response;
|
||||
mod request;
|
||||
|
||||
pub use method::Method;
|
||||
pub use error::Error;
|
||||
pub use response::Response;
|
||||
pub use request::Request;
|
||||
|
||||
use hyper::server::Handler as HypHandler;
|
||||
use hyper::server::Request as HypRequest;
|
||||
|
@ -14,9 +18,6 @@ use hyper::Server;
|
|||
|
||||
pub type Handler = fn(Request) -> Response;
|
||||
|
||||
pub struct Request;
|
||||
pub struct Response;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct Route<'a> {
|
||||
pub method: Method,
|
||||
|
@ -48,10 +49,9 @@ impl Rocket {
|
|||
}
|
||||
|
||||
pub fn mount(&mut self, base: &str, routes: &[&Route]) -> &mut Self {
|
||||
println!("Mounting at {}", base);
|
||||
println!("🛰 Mounting '{}':", base);
|
||||
for route in routes {
|
||||
println!(" - Found {} route to {}", route.method, route.path);
|
||||
(route.handler)(Request);
|
||||
println!("\t* {} '{}'", route.method, route.path);
|
||||
}
|
||||
|
||||
self
|
||||
|
@ -64,7 +64,7 @@ impl Rocket {
|
|||
|
||||
pub fn launch(self) {
|
||||
let full_addr = format!("{}:{}", self.address, self.port);
|
||||
println!("🚀 Rocket is launching ({})...", full_addr);
|
||||
println!("🚀 Rocket has launched from {}...", full_addr);
|
||||
let _ = Server::http(full_addr.as_str()).unwrap().handle(self);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use self::Method::{Options, Get, Post, Put, Delete, Head, Trace, Connect, Patch};
|
||||
use self::Method::*;
|
||||
use std::str::FromStr;
|
||||
use std::fmt::{self, Display};
|
||||
use error::Error;
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
use std::str::FromStr;
|
||||
use error::Error;
|
||||
|
||||
pub struct Request;
|
||||
|
||||
impl Request {
|
||||
pub fn get_param_str(&self, name: &str) -> Result<&str, Error> {
|
||||
Err(Error::NoKey)
|
||||
}
|
||||
|
||||
pub fn get_param<T: FromStr>(&self, name: &str) -> Result<T, Error> {
|
||||
self.get_param_str(name).and_then(|s| {
|
||||
T::from_str(s).map_err(|_| Error::BadParse)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
pub struct Response;
|
||||
|
||||
impl<'a> From<&'a str> for Response {
|
||||
fn from(_s: &'a str) -> Self {
|
||||
Response
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Response {
|
||||
fn from(_s: String) -> Self {
|
||||
Response
|
||||
}
|
||||
}
|
||||
|
||||
impl Response {
|
||||
pub fn error(number: usize) -> Response {
|
||||
println!("ERROR {}!", number);
|
||||
Response
|
||||
}
|
||||
}
|
|
@ -6,178 +6,20 @@ extern crate rustc;
|
|||
extern crate rustc_plugin;
|
||||
extern crate rocket;
|
||||
|
||||
#[macro_use] mod macro_utils;
|
||||
#[macro_use] mod utils;
|
||||
mod routes_macro;
|
||||
mod route_decorator;
|
||||
|
||||
use macro_utils::{prepend_ident, get_key_values};
|
||||
|
||||
use std::str::FromStr;
|
||||
use rustc_plugin::Registry;
|
||||
|
||||
use syntax::parse::token::{intern};
|
||||
use syntax::ext::base::SyntaxExtension;
|
||||
use syntax::ext::quote::rt::ToTokens;
|
||||
use syntax::codemap::Span;
|
||||
use syntax::ast::{Item, ItemKind, MetaItem, MetaItemKind, FnDecl, LitKind};
|
||||
use syntax::ext::base::{Annotatable, ExtCtxt};
|
||||
use syntax::ptr::P;
|
||||
use syntax::parse::token::intern;
|
||||
|
||||
use syntax::ast::{Path, PathSegment, Expr, ExprKind, TokenTree};
|
||||
use syntax::ext::base::{DummyResult, MacResult, MacEager};
|
||||
use syntax::ext::build::AstBuilder; // trait for expr_usize
|
||||
use syntax::parse::parser::{Parser, PathParsingMode};
|
||||
use syntax::parse::PResult;
|
||||
use syntax::parse::token::Token;
|
||||
|
||||
const DEBUG: bool = true;
|
||||
use routes_macro::routes_macro;
|
||||
use route_decorator::route_decorator;
|
||||
|
||||
const STRUCT_PREFIX: &'static str = "ROCKET_ROUTE_STRUCT_";
|
||||
const FN_PREFIX: &'static str = "rocket_route_fn_";
|
||||
|
||||
use rocket::Method;
|
||||
|
||||
struct Params {
|
||||
method: Method,
|
||||
path: String
|
||||
}
|
||||
|
||||
fn bad_item_fatal(ecx: &mut ExtCtxt, dec_sp: Span, i_sp: Span) -> ! {
|
||||
ecx.span_err(dec_sp, "This decorator cannot be used on non-functions...");
|
||||
ecx.span_fatal(i_sp, "...but an attempt to use it on the item below was made.")
|
||||
}
|
||||
|
||||
fn bad_method_err(ecx: &mut ExtCtxt, dec_sp: Span, message: &str) -> Method {
|
||||
let message = format!("{} Valid methods are: [GET, PUT, POST, DELETE, \
|
||||
OPTIONS, HEAD, TRACE, CONNECT, PATCH]", message);
|
||||
ecx.span_err(dec_sp, message.as_str());
|
||||
Method::Get
|
||||
}
|
||||
|
||||
fn get_fn_decl<'a>(ecx: &mut ExtCtxt, sp: Span, annotated: &'a Annotatable)
|
||||
-> (&'a P<Item>, &'a P<FnDecl>) {
|
||||
// `annotated` is the AST object for the annotated item.
|
||||
let item: &P<Item> = match annotated {
|
||||
&Annotatable::Item(ref item) => item,
|
||||
&Annotatable::TraitItem(ref item) => bad_item_fatal(ecx, sp, item.span),
|
||||
&Annotatable::ImplItem(ref item) => bad_item_fatal(ecx, sp, item.span)
|
||||
};
|
||||
|
||||
let fn_decl: &P<FnDecl> = match item.node {
|
||||
ItemKind::Fn(ref decl, _, _, _, _, _) => decl,
|
||||
_ => bad_item_fatal(ecx, sp, item.span)
|
||||
};
|
||||
|
||||
(item, fn_decl)
|
||||
}
|
||||
|
||||
fn get_route_params(ecx: &mut ExtCtxt, meta_item: &MetaItem) -> Params {
|
||||
// First, check that the macro was used in the #[route(a, b, ..)] form.
|
||||
let params: &Vec<P<MetaItem>> = match meta_item.node {
|
||||
MetaItemKind::List(_, ref params) => params,
|
||||
_ => ecx.span_fatal(meta_item.span,
|
||||
"incorrect use of macro. correct form is: #[demo(...)]"),
|
||||
};
|
||||
|
||||
// Ensure we can unwrap the k = v params.
|
||||
if params.len() < 1 {
|
||||
bad_method_err(ecx, meta_item.span, "HTTP method parameter is missing.");
|
||||
ecx.span_fatal(meta_item.span, "At least 2 arguments are required.");
|
||||
}
|
||||
|
||||
// Get the method and the rest of the k = v params. Ensure method parameter
|
||||
// is valid. If it's not, issue an error but use "GET" to continue parsing.
|
||||
let (method_param, kv_params) = params.split_first().unwrap();
|
||||
let method = if let MetaItemKind::Word(ref word) = method_param.node {
|
||||
Method::from_str(word).unwrap_or_else(|_| {
|
||||
let message = format!("{} is not a valid method.", word);
|
||||
bad_method_err(ecx, method_param.span, message.as_str())
|
||||
})
|
||||
} else {
|
||||
bad_method_err(ecx, method_param.span, "Invalid parameter. Expected a
|
||||
valid HTTP method at this position.")
|
||||
};
|
||||
|
||||
// Now grab all of the required and optional parameters.
|
||||
let req: [&'static str; 1] = ["path"];
|
||||
let opt: [&'static str; 0] = [];
|
||||
let kv_pairs = get_key_values(ecx, meta_item.span, &req, &opt, kv_params);
|
||||
|
||||
// Ensure we have a path, just to keep parsing and generating errors.
|
||||
let path = kv_pairs.get("path").map_or(String::from("/"), |s| {
|
||||
String::from(*s)
|
||||
});
|
||||
|
||||
Params {
|
||||
method: method,
|
||||
path: path
|
||||
}
|
||||
}
|
||||
|
||||
fn route_decorator(ecx: &mut ExtCtxt, sp: Span, meta_item: &MetaItem,
|
||||
annotated: &Annotatable, push: &mut FnMut(Annotatable)) {
|
||||
let (item, fn_decl) = get_fn_decl(ecx, sp, annotated);
|
||||
let route_params = get_route_params(ecx, meta_item);
|
||||
|
||||
let route_fn_name = prepend_ident(FN_PREFIX, &item.ident);
|
||||
let fn_name = item.ident;
|
||||
push(Annotatable::Item(quote_item!(ecx,
|
||||
fn $route_fn_name(_req: Request) -> Response {
|
||||
let result = $fn_name();
|
||||
println!("Routing function. Result: {}", result);
|
||||
Response
|
||||
}
|
||||
).unwrap()));
|
||||
|
||||
let struct_name = prepend_ident(STRUCT_PREFIX, &item.ident);
|
||||
let path = route_params.path;
|
||||
let struct_item = quote_item!(ecx,
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub static $struct_name: Route<'static> = Route {
|
||||
method: Method::Get, // FIXME
|
||||
path: $path,
|
||||
handler: $route_fn_name
|
||||
};
|
||||
).unwrap();
|
||||
push(Annotatable::Item(struct_item));
|
||||
}
|
||||
|
||||
fn get_paths<'a>(parser: &mut Parser<'a>) -> PResult<'a, Vec<Path>> {
|
||||
if parser.eat(&Token::Eof) {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
|
||||
let mut results = Vec::new();
|
||||
loop {
|
||||
results.push(try!(parser.parse_path(PathParsingMode::NoTypesAllowed)));
|
||||
if !parser.eat(&Token::Comma) {
|
||||
try!(parser.expect(&Token::Eof));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
fn routes_macro(ecx: &mut ExtCtxt, sp: Span, args: &[TokenTree])
|
||||
-> Box<MacResult + 'static> {
|
||||
let mut parser = ecx.new_parser_from_tts(args);
|
||||
let mut paths = get_paths(&mut parser).unwrap_or_else(|mut e| {
|
||||
e.emit();
|
||||
vec![]
|
||||
});
|
||||
|
||||
// Prefix each path terminator with STRUCT_PREFIX.
|
||||
for p in &mut paths {
|
||||
let last = p.segments.len() - 1;
|
||||
let last_seg = &mut p.segments[last];
|
||||
let new_ident = prepend_ident(STRUCT_PREFIX, &last_seg.identifier);
|
||||
last_seg.identifier = new_ident;
|
||||
}
|
||||
|
||||
// Build up the P<Expr> for each &path.
|
||||
let path_exprs = paths.iter().map(|p| { quote_expr!(ecx, &$p) }).collect();
|
||||
MacEager::expr(ecx.expr_vec_slice(sp, path_exprs))
|
||||
}
|
||||
|
||||
#[plugin_registrar]
|
||||
pub fn plugin_registrar(reg: &mut Registry) {
|
||||
reg.register_syntax_extension(intern("route"),
|
||||
|
|
|
@ -0,0 +1,233 @@
|
|||
use super::{STRUCT_PREFIX, FN_PREFIX};
|
||||
use utils::{prepend_ident, get_key_values};
|
||||
|
||||
use std::str::FromStr;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use syntax::ext::quote::rt::ToTokens;
|
||||
use syntax::codemap::{Span, DUMMY_SP};
|
||||
use syntax::ast::{Ident, TokenTree, PatKind};
|
||||
use syntax::ast::{Item, Expr, ItemKind, MetaItem, MetaItemKind, FnDecl};
|
||||
use syntax::ext::base::{Annotatable, ExtCtxt};
|
||||
use syntax::ptr::P;
|
||||
use syntax::ext::build::AstBuilder;
|
||||
use syntax::print::pprust::item_to_string;
|
||||
use syntax::parse::token::{self, str_to_ident};
|
||||
|
||||
use rocket::Method;
|
||||
|
||||
#[allow(dead_code)]
|
||||
const DEBUG: bool = true;
|
||||
|
||||
struct Params {
|
||||
method: Method,
|
||||
path: String
|
||||
}
|
||||
|
||||
fn bad_item_fatal(ecx: &mut ExtCtxt, dec_sp: Span, i_sp: Span) -> ! {
|
||||
ecx.span_err(dec_sp, "This decorator cannot be used on non-functions...");
|
||||
ecx.span_fatal(i_sp, "...but it was used on the item below.")
|
||||
}
|
||||
|
||||
fn bad_method_err(ecx: &mut ExtCtxt, dec_sp: Span, message: &str) -> Method {
|
||||
let message = format!("{} Valid methods are: [GET, PUT, POST, DELETE, \
|
||||
OPTIONS, HEAD, TRACE, CONNECT, PATCH]", message);
|
||||
ecx.span_err(dec_sp, message.as_str());
|
||||
Method::Get
|
||||
}
|
||||
|
||||
fn get_fn_decl<'a>(ecx: &mut ExtCtxt, sp: Span, annotated: &'a Annotatable)
|
||||
-> (&'a P<Item>, &'a P<FnDecl>) {
|
||||
// `annotated` is the AST object for the annotated item.
|
||||
let item: &P<Item> = match annotated {
|
||||
&Annotatable::Item(ref item) => item,
|
||||
&Annotatable::TraitItem(ref item) => bad_item_fatal(ecx, sp, item.span),
|
||||
&Annotatable::ImplItem(ref item) => bad_item_fatal(ecx, sp, item.span)
|
||||
};
|
||||
|
||||
let fn_decl: &P<FnDecl> = match item.node {
|
||||
ItemKind::Fn(ref decl, _, _, _, _, _) => decl,
|
||||
_ => bad_item_fatal(ecx, sp, item.span)
|
||||
};
|
||||
|
||||
(item, fn_decl)
|
||||
}
|
||||
|
||||
fn get_route_params(ecx: &mut ExtCtxt, meta_item: &MetaItem) -> Params {
|
||||
// First, check that the macro was used in the #[route(a, b, ..)] form.
|
||||
let params: &Vec<P<MetaItem>> = match meta_item.node {
|
||||
MetaItemKind::List(_, ref params) => params,
|
||||
_ => ecx.span_fatal(meta_item.span,
|
||||
"incorrect use of macro. correct form is: #[demo(...)]"),
|
||||
};
|
||||
|
||||
// Ensure we can unwrap the k = v params.
|
||||
if params.len() < 1 {
|
||||
bad_method_err(ecx, meta_item.span, "HTTP method parameter is missing.");
|
||||
ecx.span_fatal(meta_item.span, "At least 2 arguments are required.");
|
||||
}
|
||||
|
||||
// Get the method and the rest of the k = v params. Ensure method parameter
|
||||
// is valid. If it's not, issue an error but use "GET" to continue parsing.
|
||||
let (method_param, kv_params) = params.split_first().unwrap();
|
||||
let method = if let MetaItemKind::Word(ref word) = method_param.node {
|
||||
Method::from_str(word).unwrap_or_else(|_| {
|
||||
let message = format!("{} is not a valid method.", word);
|
||||
bad_method_err(ecx, method_param.span, message.as_str())
|
||||
})
|
||||
} else {
|
||||
bad_method_err(ecx, method_param.span, "Invalid parameter. Expected a
|
||||
valid HTTP method at this position.")
|
||||
};
|
||||
|
||||
// Now grab all of the required and optional parameters.
|
||||
let req: [&'static str; 1] = ["path"];
|
||||
let opt: [&'static str; 0] = [];
|
||||
let kv_pairs = get_key_values(ecx, meta_item.span, &req, &opt, kv_params);
|
||||
|
||||
// Ensure we have a path, just to keep parsing and generating errors.
|
||||
let path = kv_pairs.get("path").map_or(String::from("/"), |s| {
|
||||
String::from(*s)
|
||||
});
|
||||
|
||||
Params {
|
||||
method: method,
|
||||
path: path
|
||||
}
|
||||
}
|
||||
|
||||
// Is there a better way to do this? I need something with ToTokens for 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,
|
||||
// and you get an 'ident not found' on compile. I also tried using the path expr
|
||||
// builder from ASTBuilder: same thing.
|
||||
fn method_variant_to_expr(ecx: &ExtCtxt, method: Method) -> P<Expr> {
|
||||
match method {
|
||||
Method::Options => quote_expr!(ecx, rocket::Method::Options),
|
||||
Method::Get => quote_expr!(ecx, rocket::Method::Get),
|
||||
Method::Post => quote_expr!(ecx, rocket::Method::Post),
|
||||
Method::Put => quote_expr!(ecx, rocket::Method::Put),
|
||||
Method::Delete => quote_expr!(ecx, rocket::Method::Delete),
|
||||
Method::Head => quote_expr!(ecx, rocket::Method::Head),
|
||||
Method::Trace => quote_expr!(ecx, rocket::Method::Trace),
|
||||
Method::Connect => quote_expr!(ecx, rocket::Method::Connect),
|
||||
Method::Patch => quote_expr!(ecx, rocket::Method::Patch),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_fn_params(ecx: &ExtCtxt, sp: Span, path: &str,
|
||||
fn_decl: &FnDecl) -> Vec<String> {
|
||||
let mut seen = HashSet::new();
|
||||
let bad_match_err = "Path string is malformed.";
|
||||
let mut matching = false;
|
||||
|
||||
// Collect all of the params in the path and insert into HashSet.
|
||||
let mut start = 0;
|
||||
for (i, c) in path.char_indices() {
|
||||
match c {
|
||||
'<' if !matching => {
|
||||
matching = true;
|
||||
start = i;
|
||||
},
|
||||
'>' if matching => {
|
||||
matching = false;
|
||||
if start + 1 < i {
|
||||
let param_name = &path[(start + 1)..i];
|
||||
seen.insert(param_name);
|
||||
} else {
|
||||
ecx.span_err(sp, "Parameter cannot be empty.");
|
||||
}
|
||||
},
|
||||
'<' if matching => ecx.span_err(sp, bad_match_err),
|
||||
'>' if !matching => ecx.span_err(sp, bad_match_err),
|
||||
_ => { /* ... */ }
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure every param in the function declaration is in `path`. Also add
|
||||
// each param name in the declaration to the result vector.
|
||||
let mut result = vec![];
|
||||
for arg in &fn_decl.inputs {
|
||||
let ident: &Ident = match arg.pat.node {
|
||||
PatKind::Ident(_, ref ident, _) => &ident.node,
|
||||
_ => {
|
||||
ecx.span_err(sp, "Expected an identifier."); // FIXME: fn span.
|
||||
return result
|
||||
}
|
||||
};
|
||||
|
||||
let name = ident.to_string();
|
||||
if !seen.remove(name.as_str()) {
|
||||
let msg = format!("'{}' appears in the function declaration but \
|
||||
not in the path string.", name);
|
||||
ecx.span_err(sp, msg.as_str());
|
||||
}
|
||||
|
||||
result.push(name);
|
||||
}
|
||||
|
||||
// Ensure every param in `path` is in the function declaration.
|
||||
for item in seen {
|
||||
let msg = format!("'{}' appears in the path string but not in the \
|
||||
function declaration.", item);
|
||||
ecx.span_err(sp, msg.as_str());
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn route_decorator(ecx: &mut ExtCtxt, sp: Span, meta_item: &MetaItem,
|
||||
annotated: &Annotatable, push: &mut FnMut(Annotatable)) {
|
||||
let (item, fn_decl) = get_fn_decl(ecx, sp, annotated);
|
||||
let route_params = get_route_params(ecx, meta_item);
|
||||
let fn_params = get_fn_params(ecx, sp, &route_params.path, &fn_decl);
|
||||
|
||||
debug!("Path: {:?}", route_params.path);
|
||||
debug!("Function Declaration: {:?}", fn_decl);
|
||||
|
||||
let mut fn_param_exprs = vec![];
|
||||
for param in &fn_params {
|
||||
let param_ident = str_to_ident(param.as_str());
|
||||
fn_param_exprs.push(quote_stmt!(ecx,
|
||||
let $param_ident = match req.get_param($param) {
|
||||
Ok(v) => v,
|
||||
Err(_) => return rocket::Response::error(200)
|
||||
};
|
||||
).unwrap());
|
||||
}
|
||||
|
||||
let mut fn_param_idents: Vec<TokenTree> = vec![];
|
||||
for i in 0..fn_params.len() {
|
||||
let tokens = str_to_ident(fn_params[i].as_str()).to_tokens(ecx);
|
||||
fn_param_idents.extend(tokens);
|
||||
if i < fn_params.len() - 1 {
|
||||
fn_param_idents.push(TokenTree::Token(DUMMY_SP, token::Comma));
|
||||
}
|
||||
}
|
||||
|
||||
debug!("Final Params: {:?}", fn_params);
|
||||
let route_fn_name = prepend_ident(FN_PREFIX, &item.ident);
|
||||
let fn_name = item.ident;
|
||||
let route_fn_item = quote_item!(ecx,
|
||||
fn $route_fn_name(req: rocket::Request) -> rocket::Response {
|
||||
$fn_param_exprs
|
||||
let result = $fn_name($fn_param_idents);
|
||||
rocket::Response::from(result)
|
||||
}
|
||||
).unwrap();
|
||||
debug!("{}", item_to_string(&route_fn_item));
|
||||
push(Annotatable::Item(route_fn_item));
|
||||
|
||||
let struct_name = prepend_ident(STRUCT_PREFIX, &item.ident);
|
||||
let path = route_params.path;
|
||||
let method = method_variant_to_expr(ecx, route_params.method);
|
||||
push(Annotatable::Item(quote_item!(ecx,
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub static $struct_name: rocket::Route<'static> = rocket::Route {
|
||||
method: $method,
|
||||
path: $path,
|
||||
handler: $route_fn_name
|
||||
};
|
||||
).unwrap()));
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
use super::{STRUCT_PREFIX};
|
||||
use utils::prepend_ident;
|
||||
|
||||
use syntax::codemap::Span;
|
||||
use syntax::ast::{Path, TokenTree};
|
||||
use syntax::ext::base::{ExtCtxt, MacResult, MacEager};
|
||||
use syntax::ext::build::AstBuilder;
|
||||
use syntax::parse::parser::{Parser, PathParsingMode};
|
||||
use syntax::parse::PResult;
|
||||
use syntax::parse::token::Token;
|
||||
|
||||
fn get_paths<'a>(parser: &mut Parser<'a>) -> PResult<'a, Vec<Path>> {
|
||||
if parser.eat(&Token::Eof) {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
|
||||
let mut results = Vec::new();
|
||||
loop {
|
||||
results.push(try!(parser.parse_path(PathParsingMode::NoTypesAllowed)));
|
||||
if !parser.eat(&Token::Comma) {
|
||||
try!(parser.expect(&Token::Eof));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
pub fn routes_macro(ecx: &mut ExtCtxt, sp: Span, args: &[TokenTree])
|
||||
-> Box<MacResult + 'static> {
|
||||
let mut parser = ecx.new_parser_from_tts(args);
|
||||
let mut paths = get_paths(&mut parser).unwrap_or_else(|mut e| {
|
||||
e.emit();
|
||||
vec![]
|
||||
});
|
||||
|
||||
// Prefix each path terminator with STRUCT_PREFIX.
|
||||
for p in &mut paths {
|
||||
let last = p.segments.len() - 1;
|
||||
let last_seg = &mut p.segments[last];
|
||||
let new_ident = prepend_ident(STRUCT_PREFIX, &last_seg.identifier);
|
||||
last_seg.identifier = new_ident;
|
||||
}
|
||||
|
||||
// Build up the P<Expr> for each &path.
|
||||
let path_exprs = paths.iter().map(|p| { quote_expr!(ecx, &$p) }).collect();
|
||||
MacEager::expr(ecx.expr_vec_slice(sp, path_exprs))
|
||||
}
|
Loading…
Reference in New Issue