restore web proc macros

This commit is contained in:
Nikolay Kim 2020-04-10 13:28:50 +06:00
parent 95d1b4a43d
commit c1877c2707
10 changed files with 139 additions and 147 deletions

View file

@ -1,5 +1,5 @@
# Changes
## [0.1.0] - 2020-xx-xx
## [0.1.0] - 2020-04-10
* Fork
* Fork to ntex namespace

View file

@ -16,5 +16,5 @@ syn = { version = "^1", features = ["full", "parsing"] }
proc-macro2 = "^1"
[dev-dependencies]
ntex = { path = "../ntex/" }
futures = { version = "0.3.1" }
ntex = "0.1.6"
futures = "0.3.4"

View file

@ -1,26 +1,27 @@
#![recursion_limit = "512"]
//! web macros module
//!
//! Generators for routes and scopes
//! Generators for routes
//!
//! ## Route
//!
//! Macros:
//!
//! - [web_get](attr.get.html)
//! - [web_post](attr.post.html)
//! - [web_put](attr.put.html)
//! - [web_delete](attr.delete.html)
//! - [web_head](attr.head.html)
//! - [web_connect](attr.connect.html)
//! - [web_options](attr.options.html)
//! - [web_trace](attr.trace.html)
//! - [web_patch](attr.patch.html)
//! - [get](attr.web_get.html)
//! - [post](attr.web_post.html)
//! - [put](attr.web_put.html)
//! - [delete](attr.web_delete.html)
//! - [head](attr.web_head.html)
//! - [connect](attr.web_connect.html)
//! - [options](attr.web_options.html)
//! - [trace](attr.web_trace.html)
//! - [patch](attr.web_patch.html)
//!
//! ### Attributes:
//!
//! - `"path"` - Raw literal string with path for which to register handle. Mandatory.
//! - `guard="function_name"` - Registers function as guard using `ntex::web::guard::fn_guard`
//! - `guard = "function_name"` - Registers function as guard using `ntex::web::guard::fn_guard`
//! - `error = "ErrorRenderer"` - Register handler for specified error renderer
//!
//! ## Notes
//!
@ -30,11 +31,11 @@
//! ## Example:
//!
//! ```rust
//! use ntex::web::{get, HttpResponse};
//! use ntex::web::{get, Error, HttpResponse};
//! use futures::{future, Future};
//!
//! #[get("/test")]
//! async fn async_test() -> Result<HttpResponse, ntex::http::Error> {
//! async fn async_test() -> Result<HttpResponse, Error> {
//! Ok(HttpResponse::Ok().finish())
//! }
//! ```
@ -53,11 +54,12 @@ use syn::parse_macro_input;
/// ## Attributes:
///
/// - `"path"` - Raw literal string with path for which to register handler. Mandatory.
/// - `guard="function_name"` - Registers function as guard using `ntex::web::guard::fn_guard`
/// - `guard = "function_name"` - Registers function as guard using `ntex::web::guard::fn_guard`
/// - `error = "ErrorRenderer"` - Register handler for different error renderer
#[proc_macro_attribute]
pub fn web_get(args: TokenStream, input: TokenStream) -> TokenStream {
let args = parse_macro_input!(args as syn::AttributeArgs);
let gen = match route::Route::new(args, input, route::GuardType::Get) {
let gen = match route::Route::new(args, input, route::MethodType::Get) {
Ok(gen) => gen,
Err(err) => return err.to_compile_error().into(),
};
@ -72,7 +74,7 @@ pub fn web_get(args: TokenStream, input: TokenStream) -> TokenStream {
#[proc_macro_attribute]
pub fn web_post(args: TokenStream, input: TokenStream) -> TokenStream {
let args = parse_macro_input!(args as syn::AttributeArgs);
let gen = match route::Route::new(args, input, route::GuardType::Post) {
let gen = match route::Route::new(args, input, route::MethodType::Post) {
Ok(gen) => gen,
Err(err) => return err.to_compile_error().into(),
};
@ -87,7 +89,7 @@ pub fn web_post(args: TokenStream, input: TokenStream) -> TokenStream {
#[proc_macro_attribute]
pub fn web_put(args: TokenStream, input: TokenStream) -> TokenStream {
let args = parse_macro_input!(args as syn::AttributeArgs);
let gen = match route::Route::new(args, input, route::GuardType::Put) {
let gen = match route::Route::new(args, input, route::MethodType::Put) {
Ok(gen) => gen,
Err(err) => return err.to_compile_error().into(),
};
@ -102,7 +104,7 @@ pub fn web_put(args: TokenStream, input: TokenStream) -> TokenStream {
#[proc_macro_attribute]
pub fn web_delete(args: TokenStream, input: TokenStream) -> TokenStream {
let args = parse_macro_input!(args as syn::AttributeArgs);
let gen = match route::Route::new(args, input, route::GuardType::Delete) {
let gen = match route::Route::new(args, input, route::MethodType::Delete) {
Ok(gen) => gen,
Err(err) => return err.to_compile_error().into(),
};
@ -117,7 +119,7 @@ pub fn web_delete(args: TokenStream, input: TokenStream) -> TokenStream {
#[proc_macro_attribute]
pub fn web_head(args: TokenStream, input: TokenStream) -> TokenStream {
let args = parse_macro_input!(args as syn::AttributeArgs);
let gen = match route::Route::new(args, input, route::GuardType::Head) {
let gen = match route::Route::new(args, input, route::MethodType::Head) {
Ok(gen) => gen,
Err(err) => return err.to_compile_error().into(),
};
@ -132,7 +134,7 @@ pub fn web_head(args: TokenStream, input: TokenStream) -> TokenStream {
#[proc_macro_attribute]
pub fn web_connect(args: TokenStream, input: TokenStream) -> TokenStream {
let args = parse_macro_input!(args as syn::AttributeArgs);
let gen = match route::Route::new(args, input, route::GuardType::Connect) {
let gen = match route::Route::new(args, input, route::MethodType::Connect) {
Ok(gen) => gen,
Err(err) => return err.to_compile_error().into(),
};
@ -147,7 +149,7 @@ pub fn web_connect(args: TokenStream, input: TokenStream) -> TokenStream {
#[proc_macro_attribute]
pub fn web_options(args: TokenStream, input: TokenStream) -> TokenStream {
let args = parse_macro_input!(args as syn::AttributeArgs);
let gen = match route::Route::new(args, input, route::GuardType::Options) {
let gen = match route::Route::new(args, input, route::MethodType::Options) {
Ok(gen) => gen,
Err(err) => return err.to_compile_error().into(),
};
@ -162,7 +164,7 @@ pub fn web_options(args: TokenStream, input: TokenStream) -> TokenStream {
#[proc_macro_attribute]
pub fn web_trace(args: TokenStream, input: TokenStream) -> TokenStream {
let args = parse_macro_input!(args as syn::AttributeArgs);
let gen = match route::Route::new(args, input, route::GuardType::Trace) {
let gen = match route::Route::new(args, input, route::MethodType::Trace) {
Ok(gen) => gen,
Err(err) => return err.to_compile_error().into(),
};
@ -177,7 +179,7 @@ pub fn web_trace(args: TokenStream, input: TokenStream) -> TokenStream {
#[proc_macro_attribute]
pub fn web_patch(args: TokenStream, input: TokenStream) -> TokenStream {
let args = parse_macro_input!(args as syn::AttributeArgs);
let gen = match route::Route::new(args, input, route::GuardType::Patch) {
let gen = match route::Route::new(args, input, route::MethodType::Patch) {
Ok(gen) => gen,
Err(err) => return err.to_compile_error().into(),
};

View file

@ -1,10 +1,10 @@
use proc_macro::TokenStream;
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::{quote, ToTokens, TokenStreamExt};
use syn::{AttributeArgs, Ident, NestedMeta};
use syn::{AttributeArgs, Ident, NestedMeta, Path};
#[derive(PartialEq)]
pub enum GuardType {
pub enum MethodType {
Get,
Post,
Put,
@ -16,23 +16,23 @@ pub enum GuardType {
Patch,
}
impl GuardType {
impl MethodType {
fn as_str(&self) -> &'static str {
match self {
GuardType::Get => "Get",
GuardType::Post => "Post",
GuardType::Put => "Put",
GuardType::Delete => "Delete",
GuardType::Head => "Head",
GuardType::Connect => "Connect",
GuardType::Options => "Options",
GuardType::Trace => "Trace",
GuardType::Patch => "Patch",
MethodType::Get => "Get",
MethodType::Post => "Post",
MethodType::Put => "Put",
MethodType::Delete => "Delete",
MethodType::Head => "Head",
MethodType::Connect => "Connect",
MethodType::Options => "Options",
MethodType::Trace => "Trace",
MethodType::Patch => "Patch",
}
}
}
impl ToTokens for GuardType {
impl ToTokens for MethodType {
fn to_tokens(&self, stream: &mut TokenStream2) {
let ident = self.as_str();
let ident = Ident::new(ident, Span::call_site());
@ -43,12 +43,14 @@ impl ToTokens for GuardType {
struct Args {
path: syn::LitStr,
guards: Vec<Ident>,
error: Path,
}
impl Args {
fn new(args: AttributeArgs) -> syn::Result<Self> {
let mut path = None;
let mut guards = Vec::new();
let mut error: Option<Path> = None;
for arg in args {
match arg {
NestedMeta::Lit(syn::Lit::Str(lit)) => match path {
@ -72,10 +74,19 @@ impl Args {
"Attribute guard expects literal string!",
));
}
} else if nv.path.is_ident("error") {
if let syn::Lit::Str(lit) = nv.lit {
error = Some(syn::parse_str(&lit.value())?);
} else {
return Err(syn::Error::new_spanned(
nv.lit,
"Attribute error expects type path!",
));
}
} else {
return Err(syn::Error::new_spanned(
nv.path,
"Unknown attribute key is specified. Allowed: guard",
"Unknown attribute key is specified. Allowed: guard or error",
));
}
}
@ -87,6 +98,8 @@ impl Args {
Ok(Args {
path: path.unwrap(),
guards,
error: error
.unwrap_or_else(|| syn::parse_str("ntex::web::DefaultError").unwrap()),
})
}
}
@ -95,21 +108,21 @@ pub struct Route {
name: syn::Ident,
args: Args,
ast: syn::ItemFn,
guard: GuardType,
method: MethodType,
}
impl Route {
pub fn new(
args: AttributeArgs,
input: TokenStream,
guard: GuardType,
method: MethodType,
) -> syn::Result<Self> {
if args.is_empty() {
return Err(syn::Error::new(
Span::call_site(),
format!(
r#"invalid server definition, expected #[{}("<some path>")]"#,
guard.as_str().to_ascii_lowercase()
method.as_str().to_ascii_lowercase()
),
));
}
@ -121,64 +134,35 @@ impl Route {
name,
args,
ast,
guard,
method,
})
}
pub fn generate(&self) -> TokenStream {
let name = &self.name;
let resource_name = name.to_string();
let guard = &self.guard;
let ast = &self.ast;
let path = &self.args.path;
let extra_guards = &self.args.guards;
let args: Vec<_> = ast
.sig
.inputs
.iter()
.filter(|item| match item {
syn::FnArg::Receiver(_) => false,
syn::FnArg::Typed(_) => true,
})
.map(|item| match item {
syn::FnArg::Receiver(_) => panic!(),
syn::FnArg::Typed(ref pt) => pt.ty.clone(),
})
.collect();
let assert_responder: syn::Path = syn::parse_str(
format!(
"ntex::web::dev::__assert_handler{}",
if args.is_empty() {
"".to_string()
} else {
format!("{}", args.len())
}
)
.as_str(),
)
.unwrap();
let error = &self.args.error;
let method = &self.method;
let stream = quote! {
#[allow(non_camel_case_types)]
pub struct #name;
impl<__E: 'static> ntex::web::dev::HttpServiceFactory<__E> for #name
where __E: ntex::web::error::ErrorRenderer
impl ntex::web::dev::WebServiceFactory<#error> for #name
{
fn register(self, __config: &mut ntex::web::dev::AppService<__E>) {
#(ntex::web::dev::__assert_extractor::<__E, #args>();)*
fn register(self, __config: &mut ntex::web::dev::WebServiceConfig<#error>) {
#ast
let __resource = ntex::web::Resource::new(#path)
.name(#resource_name)
.guard(ntex::web::guard::#guard())
.guard(ntex::web::guard::#method())
#(.guard(ntex::web::guard::fn_guard(#extra_guards)))*
.to(#assert_responder(#name));
.to(#name);
ntex::web::dev::HttpServiceFactory::register(__resource, __config)
ntex::web::dev::WebServiceFactory::register(__resource, __config)
}
}
};

View file

@ -1,82 +1,80 @@
use futures::{future, Future};
use ntex::http::{Error, Method, StatusCode};
use ntex::web::{test, types::Path, App, HttpResponse, Responder};
use ntex_web_macros::{connect, delete, get, head, options, patch, post, put, trace};
use ntex::http::{Method, StatusCode};
use ntex::web::{test, types::Path, App, Error, HttpResponse, HttpResponseBuilder};
use ntex_macros::{
web_connect, web_delete, web_get, web_head, web_options, web_patch, web_post,
web_put, web_trace,
};
// Make sure that we can name function as 'config'
#[get("/config")]
async fn config() -> impl Responder {
HttpResponse::Ok()
#[web_get("/config")]
async fn config() -> HttpResponse {
HttpResponse::Ok().finish()
}
#[get("/test")]
async fn test_handler() -> impl Responder {
HttpResponse::Ok()
#[web_get("/test")]
async fn test_handler() -> HttpResponse {
HttpResponse::Ok().finish()
}
#[put("/test")]
async fn put_test() -> impl Responder {
HttpResponse::Created()
#[web_put("/test")]
async fn put_test() -> HttpResponse {
HttpResponse::Created().finish()
}
#[patch("/test")]
async fn patch_test() -> impl Responder {
HttpResponse::Ok()
#[web_patch("/test")]
async fn patch_test() -> HttpResponse {
HttpResponse::Ok().finish()
}
#[post("/test")]
async fn post_test() -> impl Responder {
HttpResponse::NoContent()
#[web_post("/test")]
async fn post_test() -> HttpResponse {
HttpResponse::NoContent().finish()
}
#[head("/test")]
async fn head_test() -> impl Responder {
HttpResponse::Ok()
#[web_head("/test")]
async fn head_test() -> HttpResponse {
HttpResponse::Ok().finish()
}
#[connect("/test")]
async fn connect_test() -> impl Responder {
HttpResponse::Ok()
#[web_connect("/test")]
async fn connect_test() -> HttpResponse {
HttpResponse::Ok().finish()
}
#[options("/test")]
async fn options_test() -> impl Responder {
HttpResponse::Ok()
#[web_options("/test")]
async fn options_test() -> HttpResponse {
HttpResponse::Ok().finish()
}
#[trace("/test")]
async fn trace_test() -> impl Responder {
HttpResponse::Ok()
#[web_trace("/test")]
async fn trace_test() -> HttpResponse {
HttpResponse::Ok().finish()
}
#[get("/test")]
#[web_get("/test")]
fn auto_async() -> impl Future<Output = Result<HttpResponse, Error>> {
future::ok(HttpResponse::Ok().finish())
}
#[get("/test")]
fn auto_sync() -> impl Future<Output = Result<HttpResponse, Error>> {
future::ok(HttpResponse::Ok().finish())
}
#[put("/test/{param}")]
async fn put_param_test(_: Path<String>) -> impl Responder {
#[web_put("/test/{param}")]
async fn put_param_test(_: Path<String>) -> HttpResponseBuilder {
HttpResponse::Created()
}
#[delete("/test/{param}")]
async fn delete_param_test(_: Path<String>) -> impl Responder {
#[web_delete("/test/{param}")]
async fn delete_param_test(_: Path<String>) -> HttpResponseBuilder {
HttpResponse::NoContent()
}
#[get("/test/{param}")]
async fn get_param_test(_: Path<String>) -> impl Responder {
HttpResponse::Ok()
#[web_get("/test/{param}")]
async fn get_param_test(_: Path<String>) -> HttpResponse {
HttpResponse::Ok().finish()
}
#[ntex::test]
async fn test_params() {
let srv = test::start(|| {
let srv = test::server(|| {
App::new()
.service(get_param_test)
.service(put_param_test)
@ -98,7 +96,7 @@ async fn test_params() {
#[ntex::test]
async fn test_body() {
let srv = test::start(|| {
let srv = test::server(|| {
App::new()
.service(post_test)
.service(put_test)
@ -150,7 +148,7 @@ async fn test_body() {
#[ntex::test]
async fn test_auto_async() {
let srv = test::start(|| App::new().service(auto_async));
let srv = test::server(|| App::new().service(auto_async));
let request = srv.request(Method::GET, srv.url("/test"));
let response = request.send().await.unwrap();