1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-05-19 16:58:14 +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 proc_macro::TokenStream;
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::{quote, ToTokens, TokenStreamExt};
use syn::{punctuated::Punctuated, Ident, LitStr, Path, Token, ItemFn};
use syn::Item::Fn; // todo: cleanup
use syn::Item::Fn;
use syn::{punctuated::Punctuated, Ident, ItemFn, LitStr, Path, Token}; // todo: cleanup
#[derive(Debug)]
pub struct RouteArgs {
@ -556,72 +556,83 @@ fn input_and_compile_error(mut item: TokenStream, err: syn::Error) -> TokenStrea
item
}
// todo: regular new scoped path test...
// todo: test with enums in module...
// todo: test with different namespaces...
// todo: test with multiple get/post methods...
// 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: 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: #[actix_web::get("/test")] the namespace is messing matching up
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() {
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
let mut ast: syn::ItemMod = syn::parse(input.clone()).expect("expecting module for macro scope attribute");
match scope_path {
Ok(scope_path) => { // scope_path : LitStr
if let Some((_, ref mut all_items)) = ast.content {
for item in all_items.iter_mut() {
// Expect macro to be for a module
match syn::parse::<syn::ItemMod>(input) {
Ok(mut ast) => {
// Modify the attributes of functions within the module with method with scope, if any
if let Some((_, ref mut items)) = ast.content {
items.iter_mut().for_each(|item| {
if let Fn(fun) = item {
let mut new_attrs = Vec::new();
for attr in fun.attrs.iter() {
if let Ok(_method) = MethodType::from_path(attr.path()) {
if let Ok(route_args) = attr.parse_args::<RouteArgs>() {
if let syn::Meta::List(meta_list) = attr.clone().meta {
let modified_path = format!("{}{}", scope_path.value(), route_args.path.value());
let modified_tokens = quote! {
#modified_path
};
let new_attr = syn::Attribute {
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;
fun.attrs = fun
.attrs
.iter()
.map(|attr| {
modify_attribute_with_scope(
attr,
&scope_path.clone().unwrap().value(),
)
})
.collect();
}
}
})
}
},
Err(err) => {
return input_and_compile_error(args, err);
TokenStream::from(quote! { #ast })
}
};
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")]
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::{
connect, delete, head, options, patch, put, trace, web, web::Json, HttpRequest,
get, post, connect, delete, head, options, patch, put, trace, web, HttpRequest,
HttpResponse, Responder,
};
#[actix_web::get("/test")]
#[get("/test")]
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 {
let int_value: i32 = value.parse().unwrap_or(0);
let doubled = int_value * 2;
HttpResponse::Ok().body(format!("Twice value: {}", doubled))
}
#[actix_web::post("/test")]
#[post("/test")]
pub async fn test_post() -> impl Responder {
HttpResponse::Ok().body(format!("post works"))
}
@ -464,31 +451,31 @@ const mod_inner: () = {
pub fn mod_common(message: String) -> impl actix_web::Responder {
HttpResponse::Ok().body(message)
}
};
}
#[scope("/v1")]
const mod_inner_v1: () = {
use actix_web::Responder;
mod mod_inner_v1 {
use actix_web::{get, Responder};
#[actix_web::get("/test")]
#[get("/test")]
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")]
const mod_inner_v2: () = {
use actix_web::Responder;
mod mod_inner_v2 {
use actix_web::{get, Responder};
#[actix_web::get("/test")]
#[get("/test")]
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]
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 response = request.send().await.unwrap();
@ -497,7 +484,7 @@ async fn test_scope_get_async() {
#[actix_rt::test]
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 mut response = request.send().await.unwrap();
@ -508,7 +495,7 @@ async fn test_scope_get_param_async() {
#[actix_rt::test]
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 mut response = request.send().await.unwrap();
@ -519,7 +506,7 @@ async fn test_scope_post_async() {
#[actix_rt::test]
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 mut response = request.send().await.unwrap();
@ -530,7 +517,7 @@ async fn test_scope_put_async() {
#[actix_rt::test]
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 response = request.send().await.unwrap();
@ -539,7 +526,7 @@ async fn test_scope_head_async() {
#[actix_rt::test]
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 mut response = request.send().await.unwrap();
@ -550,7 +537,7 @@ async fn test_scope_connect_async() {
#[actix_rt::test]
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 mut response = request.send().await.unwrap();
@ -561,7 +548,7 @@ async fn test_scope_options_async() {
#[actix_rt::test]
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 mut response = request.send().await.unwrap();
@ -572,7 +559,7 @@ async fn test_scope_trace_async() {
#[actix_rt::test]
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 mut response = request.send().await.unwrap();
@ -583,7 +570,7 @@ async fn test_scope_patch_async() {
#[actix_rt::test]
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 mut response = request.send().await.unwrap();
@ -594,7 +581,7 @@ async fn test_scope_delete_async() {
#[actix_rt::test]
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 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();
assert_eq!(body_str, "version2 works");
}
*/