Skip to content

Commit

Permalink
Reimplement 'routes!' and 'catchers!' as proc-macros.
Browse files Browse the repository at this point in the history
  • Loading branch information
jebrosen authored and SergioBenitez committed Sep 17, 2018
1 parent 46afabd commit 8e77961
Show file tree
Hide file tree
Showing 74 changed files with 264 additions and 176 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,5 @@ members = [
"examples/raw_sqlite",
"examples/tls",
"examples/fairings",
"examples/hello_2018",
]
4 changes: 2 additions & 2 deletions contrib/lib/tests/templates.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#![feature(plugin, decl_macro)]
#![feature(plugin, decl_macro, proc_macro_non_items)]
#![plugin(rocket_codegen)]

extern crate rocket;
#[macro_use] extern crate rocket;
extern crate rocket_contrib;

#[cfg(feature = "templates")]
Expand Down
2 changes: 0 additions & 2 deletions core/codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -435,8 +435,6 @@ macro_rules! register_macros {
#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
register_macros!(reg,
"routes" => routes,
"catchers" => catchers,
"uri" => uri,
"rocket_internal_uri" => uri_internal
);
Expand Down
62 changes: 2 additions & 60 deletions core/codegen/src/macros/mod.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
mod uri;

use {ROUTE_STRUCT_PREFIX, CATCH_STRUCT_PREFIX};
use utils::{sep_by_tok, ParserExt, IdentExt};
use utils::IdentExt;

use syntax::source_map::Span;
use syntax::tokenstream::TokenTree;
use syntax::ast::{Path, Expr};
use syntax::ext::base::{DummyResult, ExtCtxt, MacResult, MacEager};
use syntax::parse::token::Token;
use syntax::ptr::P;
use syntax::ast::Path;

pub use self::uri::{uri, uri_internal};

Expand All @@ -18,55 +12,3 @@ pub fn prefix_path(prefix: &str, path: &mut Path) {
let last_seg = &mut path.segments[last];
last_seg.ident = last_seg.ident.prepend(prefix);
}

#[inline]
pub fn prefix_paths(prefix: &str, paths: &mut Vec<Path>) {
for p in paths {
prefix_path(prefix, p);
}
}

pub fn prefixing_vec_macro<F>(
prefix: &str,
mut to_expr: F,
ecx: &mut ExtCtxt,
sp: Span,
args: &[TokenTree]) -> Box<MacResult + 'static>
where F: FnMut(&ExtCtxt, Path) -> P<Expr>
{
let mut parser = ecx.new_parser_from_tts(args);
match parser.parse_paths() {
Ok(mut paths) => {
// Prefix each path terminator and build up the P<Expr> for each path.
prefix_paths(prefix, &mut paths);
let path_exprs: Vec<P<Expr>> = paths.into_iter()
.map(|path| to_expr(ecx, path))
.collect();

// Now put them all in one vector and return the thing.
let path_list = sep_by_tok(ecx, &path_exprs, Token::Comma);
let output = quote_expr!(ecx, vec![$path_list]).into_inner();
MacEager::expr(P(output))
}
Err(mut e) => {
e.emit();
DummyResult::expr(sp)
}
}
}

#[rustfmt_skip]
pub fn routes(ecx: &mut ExtCtxt, sp: Span, args: &[TokenTree])
-> Box<MacResult + 'static> {
prefixing_vec_macro(ROUTE_STRUCT_PREFIX, |ecx, path| {
quote_expr!(ecx, ::rocket::Route::from(&$path))
}, ecx, sp, args)
}

#[rustfmt_skip]
pub fn catchers(ecx: &mut ExtCtxt, sp: Span, args: &[TokenTree])
-> Box<MacResult + 'static> {
prefixing_vec_macro(CATCH_STRUCT_PREFIX, |ecx, path| {
quote_expr!(ecx, ::rocket::Catcher::from(&$path))
}, ecx, sp, args)
}
2 changes: 1 addition & 1 deletion core/codegen/tests/complete-decorator.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![feature(plugin, decl_macro)]
#![feature(plugin, decl_macro, proc_macro_non_items)]
#![plugin(rocket_codegen)]

#[macro_use] extern crate rocket;
Expand Down
4 changes: 2 additions & 2 deletions core/codegen/tests/dynamic-paths.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#![feature(plugin, decl_macro)]
#![feature(plugin, decl_macro, proc_macro_non_items)]
#![plugin(rocket_codegen)]
#![allow(dead_code, unused_variables)]

extern crate rocket;
#[macro_use] extern crate rocket;

#[get("/test/<one>/<two>/<three>")]
fn get(one: String, two: usize, three: isize) -> &'static str { "hi" }
Expand Down
4 changes: 2 additions & 2 deletions core/codegen/tests/instanced-mounting.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#![feature(plugin, decl_macro)]
#![feature(plugin, decl_macro, proc_macro_non_items)]
#![plugin(rocket_codegen)]
#![allow(dead_code, unused_variables)]

extern crate rocket;
#[macro_use] extern crate rocket;

#[get("/one")]
fn one() { }
Expand Down
4 changes: 2 additions & 2 deletions core/codegen/tests/issue-1-colliding-names.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#![feature(plugin, decl_macro)]
#![feature(plugin, decl_macro, proc_macro_non_items)]
#![plugin(rocket_codegen)]

extern crate rocket;
#[macro_use] extern crate rocket;

#[get("/<todo>")]
fn todo(todo: String) -> String {
Expand Down
4 changes: 2 additions & 2 deletions core/codegen/tests/refactored_rocket_no_lint_errors.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#![feature(plugin, decl_macro)]
#![feature(plugin, decl_macro, proc_macro_non_items)]
#![plugin(rocket_codegen)]
#![allow(dead_code, unused_variables)]

extern crate rocket;
#[macro_use] extern crate rocket;

use rocket::{Rocket, State};

Expand Down
4 changes: 2 additions & 2 deletions core/codegen/tests/type-alias-lints.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#![feature(plugin, decl_macro)]
#![feature(plugin, decl_macro, proc_macro_non_items)]
#![plugin(rocket_codegen)]
#![allow(dead_code, unused_variables)]

extern crate rocket;
#[macro_use] extern crate rocket;

use rocket::State;

Expand Down
1 change: 1 addition & 0 deletions core/codegen_next/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ rev = "87ad56ba"

[dev-dependencies]
rocket = { version = "0.4.0-dev", path = "../lib" }
rocket_codegen = { version = "0.4.0-dev", path = "../codegen" }
compiletest_rs = "0.3.14"
17 changes: 17 additions & 0 deletions core/codegen_next/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mod derive;
mod attribute;
mod http_codegen;
mod syn_ext;
mod prefixing_vec;

crate use derive_utils::proc_macro2;

Expand All @@ -31,6 +32,22 @@ pub fn derive_responder(input: TokenStream) -> TokenStream {
derive::responder::derive_responder(input)
}

const ROUTE_STRUCT_PREFIX: &'static str = "static_rocket_route_info_for_";
#[proc_macro]
pub fn rocket_routes_internal(input: TokenStream) -> TokenStream {
prefixing_vec::prefixing_vec_macro(ROUTE_STRUCT_PREFIX, |path| {
quote!(::rocket::Route::from(&#path))
}, input)
}

const CATCH_STRUCT_PREFIX: &'static str = "static_rocket_catch_info_for_";
#[proc_macro]
pub fn rocket_catchers_internal(input: TokenStream) -> TokenStream {
prefixing_vec::prefixing_vec_macro(CATCH_STRUCT_PREFIX, |path| {
quote!(::rocket::Catcher::from(&#path))
}, input)
}

#[proc_macro_attribute]
pub fn catch(args: TokenStream, input: TokenStream) -> TokenStream {
attribute::catch::catch_attribute(args, input)
Expand Down
44 changes: 44 additions & 0 deletions core/codegen_next/src/prefixing_vec.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;

use syn::{Ident, Path};

use derive_utils::parser::{Parser, Seperator, Result as PResult};

#[inline]
fn prefix_path(prefix: &str, path: &mut Path) {
let mut last_seg = path.segments.last_mut().expect("last path segment");
let last_value = last_seg.value_mut();
last_value.ident = Ident::new(&format!("{}{}", prefix, last_value.ident), last_value.ident.span());
}

pub fn prefixing_vec_macro_internal<F>(prefix: &str, to_expr: F, args: TokenStream) -> PResult<TokenStream>
where F: FnMut(Path) -> TokenStream2
{
let mut parser = Parser::new(args);
let mut paths = parser.parse_sep(Seperator::Comma, |p| {
p.parse::<Path>()
})?;
parser.eof().map_err(|_| {
parser.current_span()
.error("expected `,` or `::` or end of macro invocation")
})?;

for ref mut p in &mut paths {
prefix_path(prefix, p);
}
let path_exprs: Vec<_> = paths.into_iter().map(to_expr).collect();

let tokens = quote! { vec![#(#path_exprs),*] };
Ok(tokens.into())
}

pub fn prefixing_vec_macro<F>(prefix: &str, to_expr: F, args: TokenStream) -> TokenStream
where F: FnMut(Path) -> TokenStream2
{
prefixing_vec_macro_internal(prefix, to_expr, args)
.unwrap_or_else(|diag| {
diag.emit();
(quote! { vec![] }).into()
})
}
8 changes: 8 additions & 0 deletions core/codegen_next/tests/compile-fail/catchers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#![feature(plugin, decl_macro, proc_macro_non_items)]

#[macro_use] extern crate rocket;

fn main() {
let _ = catchers![a b]; //~ ERROR expected
}

11 changes: 11 additions & 0 deletions core/codegen_next/tests/route.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#![feature(plugin, decl_macro, proc_macro_non_items)]
#![plugin(rocket_codegen)]

#[macro_use] extern crate rocket;

#[get("/")]
fn get() {}

fn main() {
rocket::ignite().mount("/", routes![get]);
}
8 changes: 8 additions & 0 deletions core/codegen_next/tests/ui-fail/routes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#![feature(plugin, decl_macro, proc_macro_non_items)]

#[macro_use] extern crate rocket;

fn main() {
let _ = routes![a b];
//~^ ERROR expected
}
8 changes: 8 additions & 0 deletions core/codegen_next/tests/ui-fail/routes.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error: expected `,` or `::` or end of macro invocation
--> $DIR/routes.rs:6:23
|
6 | let _ = routes![a b];
| ^

error: aborting due to previous error

4 changes: 2 additions & 2 deletions core/lib/src/catcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ use yansi::Color::*;
/// declared using the `catch` decorator, as follows:
///
/// ```rust
/// #![feature(plugin, decl_macro)]
/// #![feature(plugin, decl_macro, proc_macro_non_items)]
/// #![plugin(rocket_codegen)]
///
/// extern crate rocket;
/// #[macro_use] extern crate rocket;
///
/// use rocket::{catch, Request};
///
Expand Down
4 changes: 2 additions & 2 deletions core/lib/src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ pub type Outcome<'r> = outcome::Outcome<Response<'r>, Status, Data>;
/// managed state and a static route, as follows:
///
/// ```rust
/// # #![feature(plugin, decl_macro)]
/// # #![feature(plugin, decl_macro, proc_macro_non_items)]
/// # #![plugin(rocket_codegen)]
/// # extern crate rocket;
/// # #[macro_use] extern crate rocket;
/// #
/// # #[derive(Copy, Clone)]
/// # enum Kind {
Expand Down
14 changes: 12 additions & 2 deletions core/lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@
//! write Rocket applications. Here's a simple example to get you started:
//!
//! ```rust
//! #![feature(plugin, decl_macro)]
//! #![feature(plugin, decl_macro, proc_macro_non_items)]
//! #![plugin(rocket_codegen)]
//!
//! extern crate rocket;
//! #[macro_use] extern crate rocket;
//!
//! #[get("/")]
//! fn hello() -> &'static str {
Expand Down Expand Up @@ -102,6 +102,16 @@
#[allow(unused_imports)] #[macro_use] extern crate rocket_codegen_next;
#[doc(hidden)] pub use rocket_codegen_next::*;

#[macro_export]
macro_rules! routes {
($($input:tt)*) => { $crate::rocket_routes_internal![$($input)*] };
}

#[macro_export]
macro_rules! catchers {
($($input:tt)*) => { $crate::rocket_catchers_internal![$($input)*] };
}

extern crate rocket_http;
#[macro_use] extern crate log;
#[macro_use] extern crate pear;
Expand Down
4 changes: 2 additions & 2 deletions core/lib/src/request/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ use http::Status;
/// following example does just this:
///
/// ```rust
/// # #![feature(plugin, decl_macro)]
/// # #![feature(plugin, decl_macro, proc_macro_non_items)]
/// # #![plugin(rocket_codegen)]
/// # extern crate rocket;
/// # #[macro_use] extern crate rocket;
/// use rocket::State;
///
/// // In a real application, this would likely be more complex.
Expand Down
4 changes: 2 additions & 2 deletions core/lib/src/response/flash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ const FLASH_COOKIE_NAME: &str = "_flash";
/// message on both the request and response sides.
///
/// ```rust
/// # #![feature(plugin, decl_macro)]
/// # #![feature(plugin, decl_macro, proc_macro_non_items)]
/// # #![plugin(rocket_codegen)]
/// #
/// # extern crate rocket;
/// # #[macro_use] extern crate rocket;
/// #
/// use rocket::response::{Flash, Redirect};
/// use rocket::request::FlashMessage;
Expand Down
Loading

0 comments on commit 8e77961

Please sign in to comment.