1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-06-11 01:39:33 +00:00

add tests again. refactor nested code.

This commit is contained in:
Jon Lim 2024-03-31 22:37:48 -07:00
parent 455c064728
commit ab73c72596
2 changed files with 94 additions and 97 deletions

View file

@ -1,11 +1,11 @@
use std::{collections::HashSet}; use std::collections::HashSet;
use actix_router::ResourceDef; use actix_router::ResourceDef;
use proc_macro::TokenStream; use proc_macro::TokenStream;
use proc_macro2::{Span, TokenStream as TokenStream2}; use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::{quote, ToTokens, TokenStreamExt}; use quote::{quote, ToTokens, TokenStreamExt};
use syn::{punctuated::Punctuated, Ident, LitStr, Path, Token, ItemFn}; use syn::Item::Fn;
use syn::Item::Fn; // todo: cleanup use syn::{punctuated::Punctuated, Ident, ItemFn, LitStr, Path, Token}; // todo: cleanup
#[derive(Debug)] #[derive(Debug)]
pub struct RouteArgs { pub struct RouteArgs {
@ -556,72 +556,83 @@ fn input_and_compile_error(mut item: TokenStream, err: syn::Error) -> TokenStrea
item item
} }
// todo: regular new scoped path test...
// todo: test with enums in module... // todo: test with enums in module...
// todo: test with different namespaces...
// todo: test with multiple get/post methods... // todo: test with multiple get/post methods...
// todo: test with doc strings in attributes... // todo: test with doc strings in attributes...
// todo: clean up the nested tree of death, tell it about the input_and_compile_error convenience function... // todo: clean up the nested tree of death, tell it about the input_and_compile_error convenience function...
// todo: add in the group functions for convenient handling of group... // todo: add in the group functions for convenient handling of group...
// todo: see if can cleanup the Attribute with a clone plus one field change... // todo: see if can cleanup the Attribute with a clone plus one field change...
// todo: #[actix_web::get("/test")] the namespace is messing matching up
pub fn with_scope(args: TokenStream, input: TokenStream) -> TokenStream { pub fn with_scope(args: TokenStream, input: TokenStream) -> TokenStream {
// get the path of the scope argument // Attempt to parse the scope path, returning on error
if args.is_empty() { if args.is_empty() {
return input_and_compile_error( return input_and_compile_error(
args, syn::Error::new(proc_macro2::Span::call_site(), "invalid server definition, expected: #[scope(\"some path\")]")); args.clone(),
syn::Error::new(
Span::call_site(),
"Missing arguments for scope macro, expected: #[scope(\"some path\")]",
),
);
}
let scope_path = syn::parse::<LitStr>(args.clone().into());
if let Err(_err) = scope_path {
return input_and_compile_error(
args.clone(),
syn::Error::new(
Span::call_site(),
"Missing arguments for scope macro, expected: #[scope(\"some path\")]",
),
);
} }
let scope_path = syn::parse::<syn::LitStr>(args.clone()).map_err(|_| syn::Error::new(
proc_macro2::Span::call_site(),
"Scope macro needs a path to be specified.",
));
// modify the attribute of functions with method attributes in the module // Expect macro to be for a module
let mut ast: syn::ItemMod = syn::parse(input.clone()).expect("expecting module for macro scope attribute"); match syn::parse::<syn::ItemMod>(input) {
match scope_path { Ok(mut ast) => {
Ok(scope_path) => { // scope_path : LitStr // Modify the attributes of functions within the module with method with scope, if any
if let Some((_, ref mut all_items)) = ast.content { if let Some((_, ref mut items)) = ast.content {
for item in all_items.iter_mut() { items.iter_mut().for_each(|item| {
if let Fn(fun) = item { if let Fn(fun) = item {
let mut new_attrs = Vec::new(); fun.attrs = fun
for attr in fun.attrs.iter() { .attrs
if let Ok(_method) = MethodType::from_path(attr.path()) { .iter()
if let Ok(route_args) = attr.parse_args::<RouteArgs>() { .map(|attr| {
if let syn::Meta::List(meta_list) = attr.clone().meta { modify_attribute_with_scope(
let modified_path = format!("{}{}", scope_path.value(), route_args.path.value()); attr,
let modified_tokens = quote! { &scope_path.clone().unwrap().value(),
#modified_path )
}; })
let new_attr = syn::Attribute { .collect();
pound_token: attr.pound_token,
style: attr.style,
bracket_token: attr.bracket_token,
meta: syn::Meta::List(syn::MetaList {
path: meta_list.path,
delimiter: meta_list.delimiter,
tokens: modified_tokens,
})
};
new_attrs.push(new_attr);
}
else {
new_attrs.push(attr.clone());
}
}
else {
new_attrs.push(attr.clone());
}
}
else {
new_attrs.push(attr.clone());
}
}
fun.attrs = new_attrs;
} }
} })
} }
}, TokenStream::from(quote! { #ast })
Err(err) => {
return input_and_compile_error(args, err);
} }
}; Err(err) => {
input_and_compile_error(args, syn::Error::new(Span::call_site(), err.to_string()))
}
}
}
TokenStream::from(quote! { #ast }) // Check if the attribute is a method type and has a route path, then modify it
} fn modify_attribute_with_scope(attr: &syn::Attribute, scope_path: &str) -> syn::Attribute {
match (
MethodType::from_path(attr.path()),
attr.parse_args::<RouteArgs>(),
attr.clone().meta,
) {
(Ok(_method), Ok(route_args), syn::Meta::List(meta_list)) => {
let modified_path = format!("{}{}", scope_path, route_args.path.value());
let modified_tokens = quote! { #modified_path };
syn::Attribute {
meta: syn::Meta::List(syn::MetaList {
tokens: modified_tokens,
..meta_list.clone()
}),
..attr.clone()
}
}
_ => attr.clone(),
}
}

View file

@ -387,37 +387,24 @@ async fn test_wrap() {
*/ */
#[scope("/test")] #[scope("/test")]
mod scope_module { mod scope_module {
use actix_web::{HttpResponse, Responder};
use actix_web_codegen::{
connect, delete, get, head, options, patch, post, put, route, routes, scope, trace,
};
#[get("/get_test")]
async fn get_test() -> impl Responder {
HttpResponse::Ok()
}
}
/*
#[scope("/test")]
const mod_inner: () = {
use actix_web::{ use actix_web::{
connect, delete, head, options, patch, put, trace, web, web::Json, HttpRequest, get, post, connect, delete, head, options, patch, put, trace, web, HttpRequest,
HttpResponse, Responder, HttpResponse, Responder,
}; };
#[actix_web::get("/test")] #[get("/test")]
pub async fn test() -> impl Responder { pub async fn test() -> impl Responder {
mod_test2() HttpResponse::Ok().finish()
} }
#[actix_web::get("/twicetest/{value}")] #[get("/twicetest/{value}")]
pub async fn test_twice(value: web::Path<String>) -> impl actix_web::Responder { pub async fn test_twice(value: web::Path<String>) -> impl actix_web::Responder {
let int_value: i32 = value.parse().unwrap_or(0); let int_value: i32 = value.parse().unwrap_or(0);
let doubled = int_value * 2; let doubled = int_value * 2;
HttpResponse::Ok().body(format!("Twice value: {}", doubled)) HttpResponse::Ok().body(format!("Twice value: {}", doubled))
} }
#[actix_web::post("/test")] #[post("/test")]
pub async fn test_post() -> impl Responder { pub async fn test_post() -> impl Responder {
HttpResponse::Ok().body(format!("post works")) HttpResponse::Ok().body(format!("post works"))
} }
@ -464,31 +451,31 @@ const mod_inner: () = {
pub fn mod_common(message: String) -> impl actix_web::Responder { pub fn mod_common(message: String) -> impl actix_web::Responder {
HttpResponse::Ok().body(message) HttpResponse::Ok().body(message)
} }
}; }
#[scope("/v1")] #[scope("/v1")]
const mod_inner_v1: () = { mod mod_inner_v1 {
use actix_web::Responder; use actix_web::{get, Responder};
#[actix_web::get("/test")] #[get("/test")]
pub async fn test() -> impl Responder { pub async fn test() -> impl Responder {
super::mod_inner_scope::mod_common("version1 works".to_string()) super::scope_module::mod_common("version1 works".to_string())
} }
}; }
#[scope("/v2")] #[scope("/v2")]
const mod_inner_v2: () = { mod mod_inner_v2 {
use actix_web::Responder; use actix_web::{get, Responder};
#[actix_web::get("/test")] #[get("/test")]
pub async fn test() -> impl Responder { pub async fn test() -> impl Responder {
super::mod_inner_scope::mod_common("version2 works".to_string()) super::scope_module::mod_common("version2 works".to_string())
} }
}; }
#[actix_rt::test] #[actix_rt::test]
async fn test_scope_get_async() { async fn test_scope_get_async() {
let srv = actix_test::start(|| App::new().service(mod_inner)); let srv = actix_test::start(|| App::new().service(scope_module::test));
let request = srv.request(http::Method::GET, srv.url("/test/test")); let request = srv.request(http::Method::GET, srv.url("/test/test"));
let response = request.send().await.unwrap(); let response = request.send().await.unwrap();
@ -497,7 +484,7 @@ async fn test_scope_get_async() {
#[actix_rt::test] #[actix_rt::test]
async fn test_scope_get_param_async() { async fn test_scope_get_param_async() {
let srv = actix_test::start(|| App::new().service(mod_inner)); let srv = actix_test::start(|| App::new().service(scope_module::test_twice));
let request = srv.request(http::Method::GET, srv.url("/test/twicetest/4")); let request = srv.request(http::Method::GET, srv.url("/test/twicetest/4"));
let mut response = request.send().await.unwrap(); let mut response = request.send().await.unwrap();
@ -508,7 +495,7 @@ async fn test_scope_get_param_async() {
#[actix_rt::test] #[actix_rt::test]
async fn test_scope_post_async() { async fn test_scope_post_async() {
let srv = actix_test::start(|| App::new().service(mod_inner)); let srv = actix_test::start(|| App::new().service(scope_module::test_post));
let request = srv.request(http::Method::POST, srv.url("/test/test")); let request = srv.request(http::Method::POST, srv.url("/test/test"));
let mut response = request.send().await.unwrap(); let mut response = request.send().await.unwrap();
@ -519,7 +506,7 @@ async fn test_scope_post_async() {
#[actix_rt::test] #[actix_rt::test]
async fn test_scope_put_async() { async fn test_scope_put_async() {
let srv = actix_test::start(|| App::new().service(mod_inner)); let srv = actix_test::start(|| App::new().service(scope_module::test_put));
let request = srv.request(http::Method::PUT, srv.url("/test/test")); let request = srv.request(http::Method::PUT, srv.url("/test/test"));
let mut response = request.send().await.unwrap(); let mut response = request.send().await.unwrap();
@ -530,7 +517,7 @@ async fn test_scope_put_async() {
#[actix_rt::test] #[actix_rt::test]
async fn test_scope_head_async() { async fn test_scope_head_async() {
let srv = actix_test::start(|| App::new().service(mod_inner)); let srv = actix_test::start(|| App::new().service(scope_module::test_head));
let request = srv.request(http::Method::HEAD, srv.url("/test/test")); let request = srv.request(http::Method::HEAD, srv.url("/test/test"));
let response = request.send().await.unwrap(); let response = request.send().await.unwrap();
@ -539,7 +526,7 @@ async fn test_scope_head_async() {
#[actix_rt::test] #[actix_rt::test]
async fn test_scope_connect_async() { async fn test_scope_connect_async() {
let srv = actix_test::start(|| App::new().service(mod_inner)); let srv = actix_test::start(|| App::new().service(scope_module::test_connect));
let request = srv.request(http::Method::CONNECT, srv.url("/test/test")); let request = srv.request(http::Method::CONNECT, srv.url("/test/test"));
let mut response = request.send().await.unwrap(); let mut response = request.send().await.unwrap();
@ -550,7 +537,7 @@ async fn test_scope_connect_async() {
#[actix_rt::test] #[actix_rt::test]
async fn test_scope_options_async() { async fn test_scope_options_async() {
let srv = actix_test::start(|| App::new().service(mod_inner)); let srv = actix_test::start(|| App::new().service(scope_module::test_options));
let request = srv.request(http::Method::OPTIONS, srv.url("/test/test")); let request = srv.request(http::Method::OPTIONS, srv.url("/test/test"));
let mut response = request.send().await.unwrap(); let mut response = request.send().await.unwrap();
@ -561,7 +548,7 @@ async fn test_scope_options_async() {
#[actix_rt::test] #[actix_rt::test]
async fn test_scope_trace_async() { async fn test_scope_trace_async() {
let srv = actix_test::start(|| App::new().service(mod_inner)); let srv = actix_test::start(|| App::new().service(scope_module::test_trace));
let request = srv.request(http::Method::TRACE, srv.url("/test/test")); let request = srv.request(http::Method::TRACE, srv.url("/test/test"));
let mut response = request.send().await.unwrap(); let mut response = request.send().await.unwrap();
@ -572,7 +559,7 @@ async fn test_scope_trace_async() {
#[actix_rt::test] #[actix_rt::test]
async fn test_scope_patch_async() { async fn test_scope_patch_async() {
let srv = actix_test::start(|| App::new().service(mod_inner)); let srv = actix_test::start(|| App::new().service(scope_module::test_patch));
let request = srv.request(http::Method::PATCH, srv.url("/test/test")); let request = srv.request(http::Method::PATCH, srv.url("/test/test"));
let mut response = request.send().await.unwrap(); let mut response = request.send().await.unwrap();
@ -583,7 +570,7 @@ async fn test_scope_patch_async() {
#[actix_rt::test] #[actix_rt::test]
async fn test_scope_delete_async() { async fn test_scope_delete_async() {
let srv = actix_test::start(|| App::new().service(mod_inner)); let srv = actix_test::start(|| App::new().service(scope_module::test_delete));
let request = srv.request(http::Method::DELETE, srv.url("/test/test")); let request = srv.request(http::Method::DELETE, srv.url("/test/test"));
let mut response = request.send().await.unwrap(); let mut response = request.send().await.unwrap();
@ -594,7 +581,7 @@ async fn test_scope_delete_async() {
#[actix_rt::test] #[actix_rt::test]
async fn test_scope_v1_v2_async() { async fn test_scope_v1_v2_async() {
let srv = actix_test::start(|| App::new().service(mod_inner_v1).service(mod_inner_v2)); let srv = actix_test::start(|| App::new().service(mod_inner_v1::test).service(mod_inner_v2::test));
let request = srv.request(http::Method::GET, srv.url("/v1/test")); let request = srv.request(http::Method::GET, srv.url("/v1/test"));
let mut response = request.send().await.unwrap(); let mut response = request.send().await.unwrap();
@ -608,4 +595,3 @@ async fn test_scope_v1_v2_async() {
let body_str = String::from_utf8(body.to_vec()).unwrap(); let body_str = String::from_utf8(body.to_vec()).unwrap();
assert_eq!(body_str, "version2 works"); assert_eq!(body_str, "version2 works");
} }
*/