Tidy 'routes!' and 'catchers!' proc-macros.

This commit is contained in:
Sergio Benitez 2018-09-16 00:44:47 -07:00
parent 8e779610c4
commit 7926ffd6f0
13 changed files with 109 additions and 99 deletions

View File

@ -410,7 +410,6 @@ const DEBUG_ENV_VAR: &str = "ROCKET_CODEGEN_DEBUG";
const PARAM_PREFIX: &str = "rocket_param_";
const ROUTE_STRUCT_PREFIX: &str = "static_rocket_route_info_for_";
const CATCH_STRUCT_PREFIX: &str = "static_rocket_catch_info_for_";
const ROUTE_FN_PREFIX: &str = "rocket_route_fn_";
const URI_INFO_MACRO_PREFIX: &str = "rocket_uri_for_";

View File

@ -26,5 +26,4 @@ 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"

View File

@ -0,0 +1,48 @@
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use derive_utils::{syn, Spanned, Result};
use self::syn::{Path, punctuated::Punctuated, parse::Parser, token::Comma};
use syn_ext::{IdentExt, syn_to_diag};
fn _prefixed_vec(prefix: &str, input: TokenStream, ty: &TokenStream2) -> Result<TokenStream2> {
// Parse a comma-separated list of paths.
let mut paths = <Punctuated<Path, Comma>>::parse_terminated
.parse(input)
.map_err(syn_to_diag)?;
// Prefix the last segment in each path with `prefix`.
for path in paths.iter_mut() {
let mut last_seg = path.segments.last_mut().expect("last path segment");
last_seg.value_mut().ident = last_seg.value().ident.prepend(prefix);
}
// Return a `vec!` of the prefixed, mapped paths.
let prefixed_mapped_paths = paths.iter()
.map(|path| quote_spanned!(path.span().into() => #ty::from(&#path)));
Ok(quote!(vec![#(#prefixed_mapped_paths),*]))
}
fn prefixed_vec(prefix: &str, input: TokenStream, ty: TokenStream2) -> TokenStream {
let vec = _prefixed_vec(prefix, input, &ty)
.map_err(|diag| diag.emit())
.unwrap_or_else(|_| quote!(vec![]));
quote!({
let __vector: Vec<#ty> = #vec;
__vector
}).into()
}
pub static ROUTE_STRUCT_PREFIX: &str = "static_rocket_route_info_for_";
pub fn routes_macro(input: TokenStream) -> TokenStream {
prefixed_vec(ROUTE_STRUCT_PREFIX, input, quote!(::rocket::Route))
}
pub static CATCH_STRUCT_PREFIX: &str = "static_rocket_catch_info_for_";
pub fn catchers_macro(input: TokenStream) -> TokenStream {
prefixed_vec(CATCH_STRUCT_PREFIX, input, quote!(::rocket::Catcher))
}

View File

@ -9,9 +9,9 @@ extern crate rocket_http;
mod derive;
mod attribute;
mod bang;
mod http_codegen;
mod syn_ext;
mod prefixing_vec;
crate use derive_utils::proc_macro2;
@ -32,23 +32,17 @@ 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)
}
#[proc_macro]
pub fn routes(input: TokenStream) -> TokenStream {
bang::routes_macro(input)
}
#[proc_macro]
pub fn catchers(input: TokenStream) -> TokenStream {
bang::catchers_macro(input)
}

View File

@ -1,44 +0,0 @@
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()
})
}

View File

@ -29,4 +29,3 @@ impl ReturnTypeExt for syn::ReturnType {
}
}
}

View File

@ -1,8 +0,0 @@
#![feature(plugin, decl_macro, proc_macro_non_items)]
#[macro_use] extern crate rocket;
fn main() {
let _ = catchers![a b]; //~ ERROR expected
}

View File

@ -1,11 +0,0 @@
#![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]);
}

View File

@ -0,0 +1,10 @@
#![feature(proc_macro_non_items)]
#[macro_use] extern crate rocket;
fn main() {
let _ = catchers![a b]; //~ ERROR expected
let _ = catchers![];
let _ = catchers![a::, ]; //~ ERROR expected identifier
let _ = catchers![a::]; //~ ERROR expected identifier
}

View File

@ -0,0 +1,20 @@
error: expected `,`
--> $DIR/catchers.rs:6:25
|
6 | let _ = catchers![a b]; //~ ERROR expected
| ^
error: expected identifier
--> $DIR/catchers.rs:8:26
|
8 | let _ = catchers![a::, ]; //~ ERROR expected identifier
| ^
error: unexpected end of input, expected identifier
--> $DIR/catchers.rs:9:13
|
9 | let _ = catchers![a::]; //~ ERROR expected identifier
| ^^^^^^^^^^^^^^
error: aborting due to 3 previous errors

View File

@ -1,8 +1,10 @@
#![feature(plugin, decl_macro, proc_macro_non_items)]
#![feature(proc_macro_non_items)]
#[macro_use] extern crate rocket;
fn main() {
let _ = routes![a b];
//~^ ERROR expected
let _ = routes![a b]; //~ ERROR expected `,`
let _ = routes![];
let _ = routes![a::, ]; //~ ERROR expected identifier
let _ = routes![a::]; //~ ERROR expected identifier
}

View File

@ -1,8 +1,20 @@
error: expected `,` or `::` or end of macro invocation
error: expected `,`
--> $DIR/routes.rs:6:23
|
6 | let _ = routes![a b];
6 | let _ = routes![a b]; //~ ERROR expected `,`
| ^
error: aborting due to previous error
error: expected identifier
--> $DIR/routes.rs:8:24
|
8 | let _ = routes![a::, ]; //~ ERROR expected identifier
| ^
error: unexpected end of input, expected identifier
--> $DIR/routes.rs:9:13
|
9 | let _ = routes![a::]; //~ ERROR expected identifier
| ^^^^^^^^^^^^
error: aborting due to 3 previous errors

View File

@ -102,16 +102,6 @@
#[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;