mirror of https://github.com/rwf2/Rocket.git
Rocket is almost operational! `routes!` macro complete.
Here's what works so far: * The `route` decorator checks its inputs correctly. There's a nice utility for doing this, and it's working quite well at the moment. * The `route` decorator emits a `route_fn` and a `route_struct`. The `routes` * macro prepends the path terminator with the route struct prefix. The * `Rocket` library can read mount information (though not act on it properly just yet) and launch a server using Hyper.
This commit is contained in:
parent
967fcd26b2
commit
2e2cc3c216
|
@ -8,7 +8,7 @@
|
||||||
*.exe
|
*.exe
|
||||||
|
|
||||||
# Generated by Cargo
|
# Generated by Cargo
|
||||||
/target/
|
target
|
||||||
|
|
||||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||||
# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
|
# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "hello"
|
||||||
|
version = "0.0.1"
|
||||||
|
authors = ["Sergio Benitez <sb@sergio.bz>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rocket = { path = "../../lib" }
|
||||||
|
rocket_macros = { path = "../../macros" }
|
|
@ -0,0 +1,25 @@
|
||||||
|
#![feature(plugin)]
|
||||||
|
#![plugin(rocket_macros)]
|
||||||
|
|
||||||
|
extern crate rocket;
|
||||||
|
use rocket::{Rocket, Request, Response, Method, Route};
|
||||||
|
|
||||||
|
#[route(GET, path = "/hello")]
|
||||||
|
fn hello() -> &'static str {
|
||||||
|
"Hello, world!"
|
||||||
|
}
|
||||||
|
|
||||||
|
mod test {
|
||||||
|
use rocket::{Request, Response, Method, Route};
|
||||||
|
|
||||||
|
#[route(GET, path = "")]
|
||||||
|
pub fn hello() -> &'static str {
|
||||||
|
"Hello, world!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut rocket = Rocket::new("localhost", 8000);
|
||||||
|
rocket.mount("/test", routes![test::hello]);
|
||||||
|
rocket.mount_and_launch("/", routes![hello]);
|
||||||
|
}
|
|
@ -4,5 +4,4 @@ version = "0.0.1"
|
||||||
authors = ["Sergio Benitez <sb@sergio.bz>"]
|
authors = ["Sergio Benitez <sb@sergio.bz>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
hyper = "> 0.7.0"
|
hyper = "*"
|
||||||
rocket_macros = { path = "macros" }
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
pub enum Error {
|
||||||
|
BadMethod
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
extern crate hyper;
|
||||||
|
|
||||||
|
mod method;
|
||||||
|
mod error;
|
||||||
|
|
||||||
|
pub use method::Method;
|
||||||
|
pub use error::Error;
|
||||||
|
|
||||||
|
use hyper::server::Handler as HypHandler;
|
||||||
|
use hyper::server::Request as HypRequest;
|
||||||
|
use hyper::server::Response as HypResponse;
|
||||||
|
use hyper::net::Fresh as HypFresh;
|
||||||
|
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,
|
||||||
|
pub path: &'a str,
|
||||||
|
pub handler: Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct Rocket {
|
||||||
|
address: &'static str,
|
||||||
|
port: isize,
|
||||||
|
// mounts: HashMap<&'static str, Route<'a>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HypHandler for Rocket {
|
||||||
|
fn handle<'a, 'k>(&'a self, req: HypRequest<'a, 'k>,
|
||||||
|
res: HypResponse<'a, HypFresh>) {
|
||||||
|
println!("Request: {:?}", req.uri);
|
||||||
|
res.send(b"Hello World!").unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rocket {
|
||||||
|
pub fn new(address: &'static str, port: isize) -> Rocket {
|
||||||
|
Rocket {
|
||||||
|
address: address,
|
||||||
|
port: port
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mount(&mut self, base: &str, routes: &[&Route]) -> &mut Self {
|
||||||
|
println!("Mounting at {}", base);
|
||||||
|
for route in routes {
|
||||||
|
println!(" - Found {} route to {}", route.method, route.path);
|
||||||
|
(route.handler)(Request);
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mount_and_launch(mut self, base: &str, routes: &[&Route]) {
|
||||||
|
self.mount(base, routes);
|
||||||
|
self.launch();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn launch(self) {
|
||||||
|
let full_addr = format!("{}:{}", self.address, self.port);
|
||||||
|
println!("🚀 Rocket is launching ({})...", full_addr);
|
||||||
|
let _ = Server::http(full_addr.as_str()).unwrap().handle(self);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
use self::Method::{Options, Get, Post, Put, Delete, Head, Trace, Connect, Patch};
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::fmt::{self, Display};
|
||||||
|
use error::Error;
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
|
pub enum Method {
|
||||||
|
Get,
|
||||||
|
Put,
|
||||||
|
Post,
|
||||||
|
Delete,
|
||||||
|
Options,
|
||||||
|
Head,
|
||||||
|
Trace,
|
||||||
|
Connect,
|
||||||
|
Patch
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Method {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Method, Error> {
|
||||||
|
match s {
|
||||||
|
"OPTIONS" => Ok(Options),
|
||||||
|
"GET" => Ok(Get),
|
||||||
|
"POST" => Ok(Post),
|
||||||
|
"PUT" => Ok(Put),
|
||||||
|
"DELETE" => Ok(Delete),
|
||||||
|
"HEAD" => Ok(Head),
|
||||||
|
"TRACE" => Ok(Trace),
|
||||||
|
"CONNECT" => Ok(Connect),
|
||||||
|
"PATCH" => Ok(Patch),
|
||||||
|
_ => Err(Error::BadMethod)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Method {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
fmt.write_str(match *self {
|
||||||
|
Options => "OPTIONS",
|
||||||
|
Get => "GET",
|
||||||
|
Post => "POST",
|
||||||
|
Put => "PUT",
|
||||||
|
Delete => "DELETE",
|
||||||
|
Head => "HEAD",
|
||||||
|
Trace => "TRACE",
|
||||||
|
Connect => "CONNECT",
|
||||||
|
Patch => "PATCH",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,11 +2,9 @@
|
||||||
name = "rocket_macros"
|
name = "rocket_macros"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
authors = ["Sergio Benitez <sb@sergio.bz>"]
|
authors = ["Sergio Benitez <sb@sergio.bz>"]
|
||||||
description = "Core Rocket Macros"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
hyper = "> 0.0.0"
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "rocket_macros"
|
|
||||||
plugin = true
|
plugin = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rocket = { path = "../lib/" }
|
||||||
|
|
|
@ -1,116 +1,59 @@
|
||||||
#![crate_type = "dylib"]
|
#![crate_type = "dylib"]
|
||||||
#![feature(plugin_registrar, rustc_private)]
|
#![feature(quote, concat_idents, plugin_registrar, rustc_private)]
|
||||||
|
|
||||||
extern crate syntax;
|
#[macro_use] extern crate syntax;
|
||||||
extern crate rustc;
|
extern crate rustc;
|
||||||
extern crate rustc_plugin;
|
extern crate rustc_plugin;
|
||||||
extern crate hyper;
|
extern crate rocket;
|
||||||
|
|
||||||
|
#[macro_use] mod macro_utils;
|
||||||
|
|
||||||
|
use macro_utils::{prepend_ident, get_key_values};
|
||||||
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use rustc_plugin::Registry;
|
use rustc_plugin::Registry;
|
||||||
|
|
||||||
use syntax::parse::token::{intern};
|
use syntax::parse::token::{intern};
|
||||||
use syntax::ext::base::SyntaxExtension;
|
use syntax::ext::base::SyntaxExtension;
|
||||||
use std::default::Default;
|
use syntax::ext::quote::rt::ToTokens;
|
||||||
|
|
||||||
use syntax::codemap::Span;
|
use syntax::codemap::Span;
|
||||||
use syntax::ast::{Item, ItemKind, MetaItem, MetaItemKind, FnDecl, LitKind};
|
use syntax::ast::{Item, ItemKind, MetaItem, MetaItemKind, FnDecl, LitKind};
|
||||||
use syntax::ext::base::{Annotatable, ExtCtxt};
|
use syntax::ext::base::{Annotatable, ExtCtxt};
|
||||||
use syntax::ptr::P;
|
use syntax::ptr::P;
|
||||||
|
|
||||||
use hyper::method::Method;
|
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;
|
||||||
|
|
||||||
fn bad_item_fatal(ecx: &mut ExtCtxt, dec_sp: Span, item_sp: Span) -> ! {
|
const DEBUG: bool = true;
|
||||||
ecx.span_err(dec_sp, "This decorator cannot be used on non-functions...");
|
|
||||||
ecx.span_fatal(item_sp, "...but an attempt to use it on the item below was made.")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bad_method_err(ecx: &mut ExtCtxt, dec_sp: Span, method: &str) {
|
const STRUCT_PREFIX: &'static str = "ROCKET_ROUTE_STRUCT_";
|
||||||
let message = format!("`{}` is not a valid method. Valid methods are: \
|
const FN_PREFIX: &'static str = "rocket_route_fn_";
|
||||||
[GET, POST, PUT, DELETE, HEAD, PATCH]", method);
|
|
||||||
ecx.span_err(dec_sp, message.as_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RouteParams {
|
use rocket::Method;
|
||||||
|
|
||||||
|
struct Params {
|
||||||
method: Method,
|
method: Method,
|
||||||
path: String,
|
path: String
|
||||||
}
|
}
|
||||||
|
|
||||||
fn demo_decorator(ecx: &mut ExtCtxt, sp: Span, meta_item: &MetaItem,
|
fn bad_item_fatal(ecx: &mut ExtCtxt, dec_sp: Span, i_sp: Span) -> ! {
|
||||||
annotated: &Annotatable, _push: &mut FnMut(Annotatable)) {
|
ecx.span_err(dec_sp, "This decorator cannot be used on non-functions...");
|
||||||
// Word: #[demo]
|
ecx.span_fatal(i_sp, "...but an attempt to use it on the item below was made.")
|
||||||
// List: #[demo(one, two, ..)] or #[demo(one = "1", ...)] or mix both
|
|
||||||
// NameValue: #[demo = "1"]
|
|
||||||
let params: &Vec<P<MetaItem>> = match meta_item.node {
|
|
||||||
MetaItemKind::List(_, ref params) => params,
|
|
||||||
// Would almost certainly be better to use "DummyResult" here.
|
|
||||||
_ => ecx.span_fatal(meta_item.span,
|
|
||||||
"incorrect use of macro. correct form is: #[demo(...)]"),
|
|
||||||
};
|
|
||||||
|
|
||||||
if params.len() < 2 {
|
|
||||||
ecx.span_fatal(meta_item.span, "Bad invocation. Need >= 2 arguments.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let (method_param, kv_params) = params.split_first().unwrap();
|
fn bad_method_err(ecx: &mut ExtCtxt, dec_sp: Span, message: &str) -> Method {
|
||||||
let method = if let MetaItemKind::Word(ref word) = method_param.node {
|
let message = format!("{} Valid methods are: [GET, PUT, POST, DELETE, \
|
||||||
let method = Method::from_str(word).unwrap_or_else(|e| {
|
OPTIONS, HEAD, TRACE, CONNECT, PATCH]", message);
|
||||||
Method::Extension(String::from(&**word))
|
ecx.span_err(dec_sp, message.as_str());
|
||||||
});
|
|
||||||
|
|
||||||
if let Method::Extension(ref name) = method {
|
|
||||||
bad_method_err(ecx, meta_item.span, name.as_str());
|
|
||||||
Method::Get
|
Method::Get
|
||||||
} else {
|
|
||||||
method
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Method::Get
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut route_params: RouteParams = RouteParams {
|
|
||||||
method: method,
|
|
||||||
path: String::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut found_path = false;
|
|
||||||
for param in kv_params {
|
|
||||||
if let MetaItemKind::NameValue(ref name, ref value) = param.node {
|
|
||||||
match &**name {
|
|
||||||
"path" => {
|
|
||||||
found_path = true;
|
|
||||||
if let LitKind::Str(ref string, _) = value.node {
|
|
||||||
route_params.path = String::from(&**string);
|
|
||||||
} else {
|
|
||||||
ecx.span_err(param.span, "Path value must be string.");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
ecx.span_err(param.span, "Unrecognized parameter.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
fn get_fn_decl<'a>(ecx: &mut ExtCtxt, sp: Span, annotated: &'a Annotatable)
|
||||||
ecx.span_err(param.span, "Invalid parameter. Must be key = value.");
|
-> (&'a P<Item>, &'a P<FnDecl>) {
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found_path {
|
|
||||||
ecx.span_err(meta_item.span, "`path` argument is missing.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// for param in params {
|
|
||||||
// if let MetaItemKind::Word(ref word) = param.node {
|
|
||||||
// if hyper::method::Method::from_str(word).is_ok() {
|
|
||||||
// println!("METHOD! {}", word);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// println!("WORD Param: {:?}", param);
|
|
||||||
// } else {
|
|
||||||
// println!("NOT word Param: {:?}", param);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// `annotated` is the AST object for the annotated item.
|
// `annotated` is the AST object for the annotated item.
|
||||||
let item: &P<Item> = match annotated {
|
let item: &P<Item> = match annotated {
|
||||||
&Annotatable::Item(ref item) => item,
|
&Annotatable::Item(ref item) => item,
|
||||||
|
@ -123,12 +66,121 @@ fn demo_decorator(ecx: &mut ExtCtxt, sp: Span, meta_item: &MetaItem,
|
||||||
_ => bad_item_fatal(ecx, sp, item.span)
|
_ => bad_item_fatal(ecx, sp, item.span)
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("Function arguments: {:?}", fn_decl.inputs);
|
(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]
|
#[plugin_registrar]
|
||||||
pub fn plugin_registrar(reg: &mut Registry) {
|
pub fn plugin_registrar(reg: &mut Registry) {
|
||||||
// reg.register_macro("rn", expand_rn);
|
|
||||||
reg.register_syntax_extension(intern("route"),
|
reg.register_syntax_extension(intern("route"),
|
||||||
SyntaxExtension::MultiDecorator(Box::new(demo_decorator)));
|
SyntaxExtension::MultiDecorator(Box::new(route_decorator)));
|
||||||
|
reg.register_macro("routes", routes_macro);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
use syntax::parse::{token};
|
||||||
|
use syntax::ast::{Ident, MetaItem, MetaItemKind, LitKind};
|
||||||
|
use syntax::ext::base::{ExtCtxt};
|
||||||
|
use syntax::codemap::Span;
|
||||||
|
use syntax::ptr::P;
|
||||||
|
|
||||||
|
use std::collections::{HashSet, HashMap};
|
||||||
|
|
||||||
|
// macro_rules! debug {
|
||||||
|
// ($session:expr, $span:expr, $($message:tt)*) => ({
|
||||||
|
// if cfg!(debug) {
|
||||||
|
// span_note!($session, $span, "{}:{}", file!(), line!());
|
||||||
|
// span_note!($session, $span, $($message)*);
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
macro_rules! debug {
|
||||||
|
($($message:tt)*) => ({
|
||||||
|
if DEBUG {
|
||||||
|
println!("{}:{}", file!(), line!());
|
||||||
|
println!($($message)*);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prepend_ident<T: ToString>(other: T, ident: &Ident) -> Ident {
|
||||||
|
let mut new_ident = other.to_string();
|
||||||
|
new_ident.push_str(ident.name.to_string().as_str());
|
||||||
|
token::str_to_ident(new_ident.as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn append_ident<T: ToString>(ident: &Ident, other: T) -> Ident {
|
||||||
|
let mut new_ident = ident.name.to_string();
|
||||||
|
new_ident.push_str(other.to_string().as_str());
|
||||||
|
token::str_to_ident(new_ident.as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_key_values<'b>(ecx: &mut ExtCtxt, sp: Span, required: &[&str],
|
||||||
|
optional: &[&str], kv_params: &'b [P<MetaItem>])
|
||||||
|
-> HashMap<&'b str, &'b str> {
|
||||||
|
let mut seen = HashSet::new();
|
||||||
|
let mut kv_pairs = HashMap::new();
|
||||||
|
|
||||||
|
// Collect all the kv pairs, keeping track of what we've seen.
|
||||||
|
for param in kv_params {
|
||||||
|
if let MetaItemKind::NameValue(ref name, ref value) = param.node {
|
||||||
|
if required.contains(&&**name) || optional.contains(&&**name) {
|
||||||
|
if seen.contains(&**name) {
|
||||||
|
let msg = format!("'{}' cannot be set twice.", &**name);
|
||||||
|
ecx.span_err(param.span, &msg);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
seen.insert(&**name);
|
||||||
|
if let LitKind::Str(ref string, _) = value.node {
|
||||||
|
kv_pairs.insert(&**name, &**string);
|
||||||
|
} else {
|
||||||
|
ecx.span_err(param.span, "Value must be a string.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let msg = format!("'{}' is not a valid parameter.", &**name);
|
||||||
|
ecx.span_err(param.span, &msg);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ecx.span_err(param.span, "Expected 'key = value', found:");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, trigger an error for missing `required` params.
|
||||||
|
for req in required {
|
||||||
|
if !seen.contains(req) {
|
||||||
|
let m = format!("'{}' parameter is required but is missing.", req);
|
||||||
|
ecx.span_err(sp, &m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kv_pairs
|
||||||
|
}
|
||||||
|
// pub fn find_value_for(key: &str, kv_params: &[P<MetaItem>]) -> Option<String> {
|
||||||
|
// for param in kv_params {
|
||||||
|
// if let MetaItemKind::NameValue(ref name, ref value) = param.node {
|
||||||
|
// if &**name == key {
|
||||||
|
// if let LitKind::Str(ref string, _) = value.node {
|
||||||
|
// return Some(String::from(&**string));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// None
|
||||||
|
// }
|
13
src/main.rs
13
src/main.rs
|
@ -1,13 +0,0 @@
|
||||||
#![feature(plugin)]
|
|
||||||
#![plugin(rocket_macros)]
|
|
||||||
|
|
||||||
#[route(POST, path = "/")]
|
|
||||||
fn function(_x: usize, _y: isize) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#[route(GET, path = "/")]
|
|
||||||
fn main() {
|
|
||||||
println!("Hello, world!");
|
|
||||||
function(1, 2);
|
|
||||||
}
|
|
Loading…
Reference in New Issue