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

@ -6,12 +6,14 @@ members = [
"ntex-rt",
"ntex-rt-macros",
"ntex-service",
"ntex-macros",
]
#[patch.crates-io]
#ntex = { path = "ntex" }
#ntex-codec = { path = "ntex-codec" }
#ntex-router = { path = "ntex-router" }
#ntex-rt = { path = "ntex-rt" }
#ntex-rt-macros = { path = "ntex-rt-macros" }
#ntex-service = { path = "ntex-service" }
[patch.crates-io]
ntex = { path = "ntex" }
ntex-codec = { path = "ntex-codec" }
ntex-router = { path = "ntex-router" }
ntex-rt = { path = "ntex-rt" }
ntex-rt-macros = { path = "ntex-rt-macros" }
ntex-service = { path = "ntex-service" }
ntex-macros = { path = "ntex-macros" }

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();

View file

@ -41,6 +41,7 @@ ntex-rt = "0.1"
ntex-rt-macros = "0.1"
ntex-router = "0.3.2"
ntex-service = "0.1"
ntex-macros = "0.1"
actix-threadpool = "0.3.1"
base64 = "0.12"

View file

@ -1,7 +1,7 @@
use ntex::http;
use ntex::web::{self, middleware, App, HttpRequest, HttpResponse, HttpServer};
// #[get("/resource1/{name}/index.html")]
#[web::get("/resource1/{name}/index.html")]
async fn index(req: HttpRequest, name: web::types::Path<String>) -> String {
println!("REQ: {:?}", req);
format!("Hello: {}!\r\n", name)
@ -12,7 +12,7 @@ async fn index_async(req: HttpRequest) -> &'static str {
"Hello world!\r\n"
}
// #[get("/")]
#[web::get("/")]
async fn no_params() -> &'static str {
"Hello world!\r\n"
}
@ -24,11 +24,8 @@ async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
// .wrap(middleware::Logger::default())
.service(web::resource("/resource1/{name}/index.html").to(index))
.service(web::resource("/").route(web::get().to(no_params)))
// .service(index)
// .service(no_params)
.wrap(middleware::Logger::default())
.service((index, no_params))
.service(
web::resource("/resource2/index.html")
.wrap(

View file

@ -1,6 +1,6 @@
use ntex::web::{self, middleware, App, Error, HttpRequest, HttpResponse, HttpServer};
// #[get("/resource1/{name}/index.html")]
#[web::get("/resource1/{name}/index.html")]
async fn index(req: HttpRequest, name: web::types::Path<String>) -> String {
println!("REQ: {:?}", req);
format!("Hello: {}!\r\n", name)
@ -11,7 +11,7 @@ async fn index_async(req: HttpRequest) -> Result<&'static str, Error> {
Ok("Hello world!\r\n")
}
// #[get("/")]
#[web::get("/")]
async fn no_params() -> &'static str {
"Hello world!\r\n"
}
@ -26,10 +26,7 @@ async fn main() -> std::io::Result<()> {
App::new()
.wrap(middleware::DefaultHeaders::new().header("X-Version", "0.2"))
.wrap(middleware::Logger::default())
.service(web::resource("/resource1/{name}/index.html").to(index))
.service(web::resource("/").route(web::get().to(no_params)))
// .service(index)
// .service(no_params)
.service((index, no_params))
.service(
web::resource("/resource2/index.html")
.wrap(

View file

@ -87,6 +87,17 @@ pub mod test;
pub mod types;
mod util;
// re-export proc macro
pub use ntex_macros::web_connect as connect;
pub use ntex_macros::web_delete as delete;
pub use ntex_macros::web_get as get;
pub use ntex_macros::web_head as head;
pub use ntex_macros::web_options as options;
pub use ntex_macros::web_patch as patch;
pub use ntex_macros::web_post as post;
pub use ntex_macros::web_put as put;
pub use ntex_macros::web_trace as trace;
pub use crate::http::Response as HttpResponse;
pub use crate::http::ResponseBuilder as HttpResponseBuilder;