1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-05-08 03:23:06 +00:00

Compare commits

...

42 commits

Author SHA1 Message Date
Jonathan Lim e6611ffa69
Merge d35801cf12 into ba7fd048b6 2024-04-17 05:58:01 +00:00
Jon Lim d35801cf12 needed to format code 2024-04-16 22:57:44 -07:00
Jon Lim 89f78190bd sync to master branch by adding test_wrap 2024-04-16 22:55:37 -07:00
Jonathan Lim 6d3fff281b
Merge branch 'master' into scope_work 2024-04-16 22:46:57 -07:00
Jonathan Lim 6eabb23bac
Merge branch 'master' into scope_work 2024-04-08 18:28:00 -07:00
Jon Lim 2f4b859dab fix out of date comment for scope macro 2024-04-02 22:39:13 -07:00
Jon Lim bcc0bed4f5 clean up for rust fmt 2024-04-02 22:21:04 -07:00
Jon Lim 4fb51ad70a clean up for rust fmt 2024-04-02 22:19:25 -07:00
Jon Lim 828be28199 clean up code. fix bugs with route and method attributes with parameters 2024-04-02 22:13:13 -07:00
Jon Lim ab73c72596 add tests again. refactor nested code. 2024-03-31 22:37:48 -07:00
Jon Lim 455c064728 Merge branch 'master' into scope_work 2024-03-31 20:44:36 -07:00
Jon Lim c4520d909a work in progress. revised procedural macro to change othe macro call 2024-03-31 20:44:08 -07:00
Jon Lim f8f93d4dab Merge branch 'master' into scope_work
update CHANGES for unreleased scope macro
2024-02-18 10:52:58 -08:00
Jonathan Lim 16c84c2805
Merge branch 'master' into scope_work 2024-01-25 09:29:34 -08:00
Jonathan Lim 4a8b05c837
Merge branch 'master' into scope_work 2024-01-11 13:46:54 -08:00
Jonathan Lim 687667f1f7
Merge branch 'master' into scope_work 2024-01-08 22:58:44 -08:00
Jonathan Lim f01b4cd68d
Merge branch 'master' into scope_work 2024-01-06 13:08:49 -08:00
Jonathan Lim 88883b781d
Merge branch 'master' into scope_work 2023-12-28 18:50:02 -08:00
Jonathan Lim f82e740776
Merge branch 'master' into scope_work 2023-12-21 19:20:49 -08:00
Jonathan Lim d6ee2779cd
Merge branch 'master' into scope_work 2023-12-16 00:20:59 -08:00
Jonathan Lim 3ee0dc9329
Update actix-web-codegen/src/lib.rs with comment documentation fix
Co-authored-by: oliver <151407407+kwfn@users.noreply.github.com>
2023-12-09 17:56:28 -08:00
Jonathan Lim f9bd3472a2
Merge branch 'master' into scope_work 2023-12-06 23:18:20 -08:00
Jonathan Lim 567deaa3ec
Merge branch 'master' into scope_work 2023-11-28 13:50:36 -08:00
Jonathan Lim 5d291e1cc1
Merge branch 'master' into scope_work 2023-11-20 07:00:54 -08:00
Jonathan Lim b05197accf
Merge branch 'master' into scope_work 2023-11-20 07:00:47 -08:00
Jonathan Lim 22e51a4287
Merge branch 'master' into scope_work 2023-11-20 07:00:32 -08:00
Jonathan Lim e3432c88ce
Merge branch 'master' into scope_work 2023-11-02 23:21:59 -07:00
Jonathan Lim 4f1135804a
Merge branch 'master' into scope_work 2023-10-23 20:01:49 -07:00
Jonathan Lim bbf5cf3cd0
Merge branch 'master' into scope_work 2023-10-15 02:24:06 -07:00
Jonathan Lim 03501cbf98
Merge branch 'master' into scope_work 2023-10-11 23:13:46 -07:00
Jon Lim 40b8ec7b97 Merge branch 'master' into scope_work 2023-10-08 20:57:01 -07:00
Jon Lim e931a58092 format code with formatter 2023-09-20 19:55:12 -07:00
Jon Lim 3c3b5d0cdf Merge branch 'master' into scope_work 2023-09-20 19:54:07 -07:00
Jonathan Lim 4ae7a00b3e
Merge branch 'master' into scope_work 2023-09-17 20:41:31 -07:00
Jon Lim 3e4c6438f3 add another test for combining and calling 2 scopes 2023-09-16 14:40:53 -07:00
Jonathan Lim 3a5714836c
Merge branch 'master' into scope_work 2023-09-16 14:24:03 -07:00
Jon Lim ec4633a911 code formatting cleanup 2023-09-13 22:45:37 -07:00
Jon Lim efe990dca5 Merge branch 'master' into scope_work 2023-09-13 21:59:45 -07:00
Jon Lim 6bedb958e4 add some unit tests 2023-09-13 21:57:54 -07:00
Jon Lim db69279557 started some test code 2023-09-10 16:13:12 -07:00
Jon Lim ac82b56ad7 Update scope macro code to work with current HttpServiceFactory 2023-09-10 14:15:41 -07:00
Jon Lim ad90bc926d add scope proc macro 2023-09-03 14:05:21 -07:00
5 changed files with 313 additions and 1 deletions

View file

@ -3,6 +3,7 @@
## Unreleased
- Minimum supported Rust version (MSRV) is now 1.72.
- Add a scope macro that takes a path
## 4.2.2

View file

@ -240,3 +240,31 @@ pub fn test(_: TokenStream, item: TokenStream) -> TokenStream {
output.extend(item);
output
}
/// Generates scope
///
/// Syntax: `#[scope("path")]`
///
/// ## Attributes:
///
/// - `"path"` - Raw literal string with path for which to register handler. Mandatory.
///
/// # Example
///
/// ```rust
/// use actix_web_codegen::{scope};
/// #[scope("/test")]
/// mod scope_module {
/// use actix_web::{get, HttpResponse, Responder};
/// #[get("/test")]
/// pub async fn test() -> impl Responder {
/// // this has path /test/test
/// HttpResponse::Ok().finish()
/// }
/// }
/// ```
///
#[proc_macro_attribute]
pub fn scope(args: TokenStream, input: TokenStream) -> TokenStream {
route::with_scope(args, input)
}

View file

@ -554,3 +554,93 @@ fn input_and_compile_error(mut item: TokenStream, err: syn::Error) -> TokenStrea
item.extend(compile_err);
item
}
pub fn with_scope(args: TokenStream, input: TokenStream) -> TokenStream {
// Attempt to parse the scope path, returning on error
if args.is_empty() {
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::<LitStr>(args.clone());
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\")]",
),
);
}
// Expect macro to be for a module
match syn::parse::<syn::ItemMod>(input) {
Ok(mut ast) => {
// Modify the attributes of functions with method or route(s) by adding scope argument as prefix, if any
if let Some((_, ref mut items)) = ast.content {
items.iter_mut().for_each(|item| {
if let syn::Item::Fn(fun) = item {
fun.attrs = fun
.attrs
.iter()
.map(|attr| {
modify_attribute_with_scope(
attr,
&scope_path.clone().unwrap().value(),
)
})
.collect();
}
})
}
TokenStream::from(quote! { #ast })
}
Err(err) => {
input_and_compile_error(args, syn::Error::new(Span::call_site(), err.to_string()))
}
}
}
fn has_allowed_methods_in_scope(attr: &syn::Attribute) -> bool {
MethodType::from_path(attr.path()).is_ok()
|| attr.path().is_ident("route")
|| attr.path().is_ident("ROUTE")
}
// 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 (attr.parse_args::<RouteArgs>(), attr.clone().meta) {
(Ok(route_args), syn::Meta::List(meta_list)) if has_allowed_methods_in_scope(attr) => {
let modified_path = format!("{}{}", scope_path, route_args.path.value());
let options_tokens: Vec<TokenStream2> = route_args
.options
.iter()
.map(|option| {
quote! { ,#option }
})
.collect();
let combined_options_tokens: TokenStream2 =
options_tokens
.into_iter()
.fold(TokenStream2::new(), |mut acc, ts| {
acc.extend(std::iter::once(ts));
acc
});
syn::Attribute {
meta: syn::Meta::List(syn::MetaList {
tokens: quote! { #modified_path #combined_options_tokens },
..meta_list.clone()
}),
..attr.clone()
}
}
_ => attr.clone(),
}
}

View file

@ -11,7 +11,7 @@ use actix_web::{
web, App, Error, HttpRequest, HttpResponse, Responder,
};
use actix_web_codegen::{
connect, delete, get, head, options, patch, post, put, route, routes, trace,
connect, delete, get, head, options, patch, post, put, route, routes, scope, trace,
};
use futures_core::future::LocalBoxFuture;
@ -384,3 +384,195 @@ async fn test_wrap() {
let body = String::from_utf8(body.to_vec()).unwrap();
assert!(body.contains("wrong number of parameters"));
}
#[scope("/test")]
mod scope_module {
use actix_web::{delete, get, post, route, routes, web, HttpResponse, Responder};
use crate::guard_module;
#[get("/test/guard", guard = "guard_module::guard")]
pub async fn test_guard() -> impl Responder {
HttpResponse::Ok()
}
#[get("/test")]
pub async fn test() -> impl Responder {
HttpResponse::Ok().finish()
}
#[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))
}
#[post("/test")]
pub async fn test_post() -> impl Responder {
HttpResponse::Ok().body(format!("post works"))
}
#[delete("/test")]
pub async fn test_delete() -> impl Responder {
HttpResponse::Ok().body("delete works")
}
#[route("/test", method = "PUT", method = "PATCH", method = "CUSTOM")]
pub async fn test_multiple_shared_path() -> impl Responder {
HttpResponse::Ok().finish()
}
#[routes]
#[head("/test")]
#[connect("/test")]
#[options("/test")]
#[trace("/test")]
async fn test_multiple_separate_paths() -> impl Responder {
HttpResponse::Ok().finish()
}
// test calling this from other mod scope with scope attribute...
pub fn mod_common(message: String) -> impl actix_web::Responder {
HttpResponse::Ok().body(message)
}
}
#[scope("/v1")]
mod mod_scope_v1 {
use actix_web::{get, Responder};
#[get("/test")]
#[doc = "doc string to check in cargo expand"]
pub async fn test() -> impl Responder {
super::scope_module::mod_common("version1 works".to_string())
}
}
#[scope("/v2")]
mod mod_scope_v2 {
use actix_web::{get, Responder};
// check to make sure non-function tokens in the scope block are preserved...
enum TestEnum {
Works,
}
#[get("/test")]
pub async fn test() -> impl Responder {
// make sure this type still exists...
let test_enum = TestEnum::Works;
match test_enum {
TestEnum::Works => 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(scope_module::test));
let request = srv.request(http::Method::GET, srv.url("/test/test"));
let response = request.send().await.unwrap();
assert!(response.status().is_success());
}
#[actix_rt::test]
async fn test_scope_get_param_async() {
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();
let body = response.body().await.unwrap();
let body_str = String::from_utf8(body.to_vec()).unwrap();
assert_eq!(body_str, "Twice value: 8");
}
#[actix_rt::test]
async fn test_scope_post_async() {
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();
let body = response.body().await.unwrap();
let body_str = String::from_utf8(body.to_vec()).unwrap();
assert_eq!(body_str, "post works");
}
#[actix_rt::test]
async fn test_multiple_shared_path_async() {
let srv = actix_test::start(|| App::new().service(scope_module::test_multiple_shared_path));
let request = srv.request(http::Method::PUT, srv.url("/test/test"));
let response = request.send().await.unwrap();
assert!(response.status().is_success());
let request = srv.request(http::Method::PATCH, srv.url("/test/test"));
let response = request.send().await.unwrap();
assert!(response.status().is_success());
}
#[actix_rt::test]
async fn test_multiple_multipaths_async() {
let srv = actix_test::start(|| App::new().service(scope_module::test_multiple_separate_paths));
let request = srv.request(http::Method::CONNECT, srv.url("/test/test"));
let response = request.send().await.unwrap();
assert!(response.status().is_success());
let request = srv.request(http::Method::OPTIONS, srv.url("/test/test"));
let response = request.send().await.unwrap();
assert!(response.status().is_success());
let request = srv.request(http::Method::TRACE, srv.url("/test/test"));
let response = request.send().await.unwrap();
assert!(response.status().is_success());
let request = srv.request(http::Method::HEAD, srv.url("/test/test"));
let response = request.send().await.unwrap();
assert!(response.status().is_success());
}
#[actix_rt::test]
async fn test_scope_delete_async() {
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();
let body = response.body().await.unwrap();
let body_str = String::from_utf8(body.to_vec()).unwrap();
assert_eq!(body_str, "delete works");
}
#[actix_rt::test]
async fn test_scope_get_with_guard_async() {
let srv = actix_test::start(|| App::new().service(scope_module::test_guard));
let request = srv
.request(http::Method::GET, srv.url("/test/test/guard"))
.insert_header(("Accept", "image/*"));
let response = request.send().await.unwrap();
assert!(response.status().is_success());
}
#[actix_rt::test]
async fn test_scope_v1_v2_async() {
let srv = actix_test::start(|| {
App::new()
.service(mod_scope_v1::test)
.service(mod_scope_v2::test)
});
let request = srv.request(http::Method::GET, srv.url("/v1/test"));
let mut response = request.send().await.unwrap();
let body = response.body().await.unwrap();
let body_str = String::from_utf8(body.to_vec()).unwrap();
assert_eq!(body_str, "version1 works");
let request = srv.request(http::Method::GET, srv.url("/v2/test"));
let mut response = request.send().await.unwrap();
let body = response.body().await.unwrap();
let body_str = String::from_utf8(body.to_vec()).unwrap();
assert_eq!(body_str, "version2 works");
}

View file

@ -142,5 +142,6 @@ codegen_reexport!(delete);
codegen_reexport!(trace);
codegen_reexport!(connect);
codegen_reexport!(options);
codegen_reexport!(scope);
pub(crate) type BoxError = Box<dyn std::error::Error>;