Skip to content

Commit

Permalink
added proc-macros for route registration
Browse files Browse the repository at this point in the history
  • Loading branch information
fafhrd91 committed Mar 7, 2019
1 parent 1151b5b commit 22708e7
Show file tree
Hide file tree
Showing 9 changed files with 221 additions and 42 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ members = [
".",
"actix-files",
"actix-session",
"actix-web-codegen",
]

[package.metadata.docs.rs]
Expand Down Expand Up @@ -63,6 +64,7 @@ actix-codec = "0.1.0"
#actix-service = "0.3.2"
#actix-utils = "0.3.1"
actix-rt = "0.2.0"
actix-web-codegen = { path="actix-web-codegen" }

actix-service = { git = "https://github.com/actix/actix-net.git" }
actix-utils = { git = "https://github.com/actix/actix-net.git" }
Expand Down
10 changes: 3 additions & 7 deletions actix-files/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,9 +318,7 @@ where
}
}

impl<P, C: StaticFileConfig + 'static> NewService<ServiceRequest<P>>
for Files<P, C>
{
impl<P, C: StaticFileConfig + 'static> NewService<ServiceRequest<P>> for Files<P, C> {
type Response = ServiceResponse;
type Error = ();
type Service = FilesService<P, C>;
Expand Down Expand Up @@ -730,8 +728,7 @@ mod tests {
#[test]
fn test_named_file_content_range_headers() {
let mut srv = test::init_service(
App::new()
.service(Files::new("/test", ".").index_file("tests/test.binary")),
App::new().service(Files::new("/test", ".").index_file("tests/test.binary")),
);

// Valid range header
Expand Down Expand Up @@ -770,8 +767,7 @@ mod tests {
#[test]
fn test_named_file_content_length_headers() {
let mut srv = test::init_service(
App::new()
.service(Files::new("test", ".").index_file("tests/test.binary")),
App::new().service(Files::new("test", ".").index_file("tests/test.binary")),
);

// Valid range header
Expand Down
15 changes: 15 additions & 0 deletions actix-web-codegen/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "actix-web-codegen"
description = "Actix web codegen macros"
version = "0.1.0"
authors = ["Nikolay Kim <[email protected]>"]
license = "MIT/Apache-2.0"
edition = "2018"
workspace = ".."

[lib]
proc-macro = true

[dependencies]
quote = "0.6"
syn = { version = "0.15", features = ["full", "parsing"] }
115 changes: 115 additions & 0 deletions actix-web-codegen/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#![recursion_limit = "512"]

extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;
use syn::parse_macro_input;

/// #[get("path")] attribute
#[proc_macro_attribute]
pub fn get(args: TokenStream, input: TokenStream) -> TokenStream {
let args = parse_macro_input!(args as syn::AttributeArgs);
if args.is_empty() {
panic!("invalid server definition, expected: #[get(\"some path\")]");
}

// path
let path = match args[0] {
syn::NestedMeta::Literal(syn::Lit::Str(ref fname)) => {
let fname = quote!(#fname).to_string();
fname.as_str()[1..fname.len() - 1].to_owned()
}
_ => panic!("resource path"),
};

let ast: syn::ItemFn = syn::parse(input).unwrap();
let name = ast.ident.clone();

(quote! {
#[allow(non_camel_case_types)]
struct #name;

impl<P: 'static> actix_web::dev::HttpServiceFactory<P> for #name {
fn register(self, config: &mut actix_web::dev::AppConfig<P>) {
#ast
actix_web::dev::HttpServiceFactory::register(
actix_web::Resource::new(#path)
.route(actix_web::web::get().to(#name)), config);
}
}
})
.into()
}

/// #[post("path")] attribute
#[proc_macro_attribute]
pub fn post(args: TokenStream, input: TokenStream) -> TokenStream {
let args = parse_macro_input!(args as syn::AttributeArgs);
if args.is_empty() {
panic!("invalid server definition, expected: #[get(\"some path\")]");
}

// path
let path = match args[0] {
syn::NestedMeta::Literal(syn::Lit::Str(ref fname)) => {
let fname = quote!(#fname).to_string();
fname.as_str()[1..fname.len() - 1].to_owned()
}
_ => panic!("resource path"),
};

let ast: syn::ItemFn = syn::parse(input).unwrap();
let name = ast.ident.clone();

(quote! {
#[allow(non_camel_case_types)]
struct #name;

impl<P: 'static> actix_web::dev::HttpServiceFactory<P> for #name {
fn register(self, config: &mut actix_web::dev::AppConfig<P>) {
#ast
actix_web::dev::HttpServiceFactory::register(
actix_web::Resource::new(#path)
.route(actix_web::web::post().to(#name)), config);
}
}
})
.into()
}

/// #[put("path")] attribute
#[proc_macro_attribute]
pub fn put(args: TokenStream, input: TokenStream) -> TokenStream {
let args = parse_macro_input!(args as syn::AttributeArgs);
if args.is_empty() {
panic!("invalid server definition, expected: #[get(\"some path\")]");
}

// path
let path = match args[0] {
syn::NestedMeta::Literal(syn::Lit::Str(ref fname)) => {
let fname = quote!(#fname).to_string();
fname.as_str()[1..fname.len() - 1].to_owned()
}
_ => panic!("resource path"),
};

let ast: syn::ItemFn = syn::parse(input).unwrap();
let name = ast.ident.clone();

(quote! {
#[allow(non_camel_case_types)]
struct #name;

impl<P: 'static> actix_web::dev::HttpServiceFactory<P> for #name {
fn register(self, config: &mut actix_web::dev::AppConfig<P>) {
#ast
actix_web::dev::HttpServiceFactory::register(
actix_web::Resource::new(#path)
.route(actix_web::web::put().to(#name)), config);
}
}
})
.into()
}
31 changes: 31 additions & 0 deletions actix-web-codegen/src/server.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use std::collections::HashSet;
use std::env;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;

use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn;

/// Thrift mux server impl
pub struct Server {}

impl Server {
fn new() -> Server {
Server {}
}

/// generate servers
pub fn generate(input: TokenStream) {
let mut srv = Server::new();
let ast: syn::ItemFn = syn::parse2(input).unwrap();
println!("T: {:?}", ast.ident);

// quote! {
// #ast

// #(#servers)*
// }
}
}
13 changes: 7 additions & 6 deletions examples/basic.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use futures::IntoFuture;

use actix_web::{
http::Method, middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer,
};
use actix_web::macros::get;
use actix_web::{middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer};

#[get("/resource1/index.html")]
fn index(req: HttpRequest) -> &'static str {
println!("REQ: {:?}", req);
"Hello world!\r\n"
Expand All @@ -14,6 +14,7 @@ fn index_async(req: HttpRequest) -> impl IntoFuture<Item = &'static str, Error =
Ok("Hello world!\r\n")
}

#[get("/")]
fn no_params() -> &'static str {
"Hello world!\r\n"
}
Expand All @@ -27,7 +28,8 @@ fn main() -> std::io::Result<()> {
App::new()
.middleware(middleware::DefaultHeaders::new().header("X-Version", "0.2"))
.middleware(middleware::Compress::default())
.service(web::resource("/resource1/index.html").route(web::get().to(index)))
.service(index)
.service(no_params)
.service(
web::resource("/resource2/index.html")
.middleware(
Expand All @@ -36,10 +38,9 @@ fn main() -> std::io::Result<()> {
.default_resource(|r| {
r.route(web::route().to(|| HttpResponse::MethodNotAllowed()))
})
.route(web::method(Method::GET).to_async(index_async)),
.route(web::get().to_async(index_async)),
)
.service(web::resource("/test1.html").to(|| "Test\r\n"))
.service(web::resource("/").to(no_params))
})
.bind("127.0.0.1:8080")?
.workers(1)
Expand Down
Loading

0 comments on commit 22708e7

Please sign in to comment.