From e12abe06aeb9d54039841799422dd46ec46ff60c Mon Sep 17 00:00:00 2001 From: Andeya Date: Sat, 28 Sep 2024 14:02:27 +0800 Subject: [PATCH 1/6] chore(craft-macros): When `self.0` is shadowed, it is convenient to directly call the `self.deref()` method. --- crates/craft-macros/src/craft.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/craft-macros/src/craft.rs b/crates/craft-macros/src/craft.rs index 70f2ea068..51f486506 100644 --- a/crates/craft-macros/src/craft.rs +++ b/crates/craft-macros/src/craft.rs @@ -133,7 +133,8 @@ fn rewrite_method( parse_quote! { #vis fn #method_name(#receiver) -> impl #handler { pub struct handle #impl_generics(::std::sync::Arc<#self_ty>) #where_clause; - impl #impl_generics ::std::ops::Deref for handle #impl_generics #where_clause{ + use ::std::ops::Deref; + impl #impl_generics Deref for handle #impl_generics #where_clause{ type Target = #self_ty; fn deref(&self) -> &Self::Target { From cdeea134b6fe51b0f971ca1023fe32ad4dab371d Mon Sep 17 00:00:00 2001 From: Andeya Date: Sun, 29 Sep 2024 14:18:34 +0800 Subject: [PATCH 2/6] chore(craft-macros): Improve the robustness of extracting attributes. --- crates/craft-macros/Cargo.toml | 3 +- crates/craft-macros/examples/example-add.rs | 2 +- crates/craft-macros/src/craft.rs | 41 +++++++++++++-------- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/crates/craft-macros/Cargo.toml b/crates/craft-macros/Cargo.toml index dca25ac53..cccc5275e 100644 --- a/crates/craft-macros/Cargo.toml +++ b/crates/craft-macros/Cargo.toml @@ -25,10 +25,11 @@ proc-macro-crate.workspace = true proc-macro2.workspace = true quote.workspace = true syn = { workspace = true, features = ["full", "parsing"] } +regex.workspace = true [dev-dependencies] salvo = { path = "../salvo", features = ["oapi"] } tokio.workspace = true [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/crates/craft-macros/examples/example-add.rs b/crates/craft-macros/examples/example-add.rs index 1514db418..da89e73c2 100644 --- a/crates/craft-macros/examples/example-add.rs +++ b/crates/craft-macros/examples/example-add.rs @@ -33,7 +33,7 @@ impl Opts { } /// doc line 5 /// doc line 6 - #[craft(endpoint(responses((status_code = 400, description = "Wrong request parameters."))))] + #[craft(endpoint(responses((status_code = 400, description = "[(Wrong)] request parameters."))))] pub fn add3(left: QueryParam, right: QueryParam) -> String { (*left + *right).to_string() } diff --git a/crates/craft-macros/src/craft.rs b/crates/craft-macros/src/craft.rs index 05de755a3..d71781b54 100644 --- a/crates/craft-macros/src/craft.rs +++ b/crates/craft-macros/src/craft.rs @@ -1,6 +1,7 @@ use crate::utils::salvo_crate; use proc_macro2::{Span, TokenStream}; use quote::{quote, ToTokens}; +use regex::Regex; use syn::parse::Parser; use syn::{ parse_quote, Attribute, FnArg, Generics, Ident, ImplItem, ImplItemFn, Item, Token, Type, @@ -28,6 +29,9 @@ pub(crate) fn generate(input: Item) -> syn::Result { } } +const REGEX_STR: &'static str = + r#"(?s)#\s*\[\s*craft\s*\(\s*(?Phandler|endpoint)\s*(?P\(.*\))?\s*\)\s*\]"#; + fn take_method_macro(item_fn: &mut ImplItemFn) -> syn::Result> { let mut index: Option = None; let mut new_attr: Option = None; @@ -38,26 +42,31 @@ fn take_method_macro(item_fn: &mut ImplItemFn) -> syn::Result> }) { continue; } - if let Some((_, last)) = attr.to_token_stream().to_string().split_once("craft(") { - if let Some(last) = last.strip_suffix(")]") { - let ts: Option = if last == "handler" || last.starts_with("handler(") { - Some(format!("#[{}::{last}]", salvo_crate()).parse()?) - } else if last == "endpoint" || last.starts_with("endpoint(") { - Some(format!("#[{}::oapi::{last}]", salvo_crate()).parse()?) - } else { - None + let re = Regex::new(REGEX_STR).unwrap(); + let attr_str = attr.to_token_stream().to_string().trim().to_owned(); + if let Some(caps) = re.captures(&attr_str) { + if let Some(name) = caps.name("name") { + let name = name.as_str(); + let content = caps + .name("content") + .map(|c| c.as_str().to_string()) + .unwrap_or_default(); + let ts: TokenStream = match name { + "handler" => format!("#[{}::{name}{content}]", salvo_crate()).parse()?, + "endpoint" => format!("#[{}::oapi::{name}{content}]", salvo_crate()).parse()?, + _ => { + unreachable!() + } }; - if let Some(ts) = ts { - new_attr = Attribute::parse_outer.parse2(ts)?.into_iter().next(); - index = Some(idx); - continue; - } + new_attr = Attribute::parse_outer.parse2(ts)?.into_iter().next(); + index = Some(idx); + continue; } } return Err(syn::Error::new_spanned( - item_fn, - "The attribute macro #[craft] on a method must be filled with sub-attributes, such as '#[craft(handler)]', '#[craft(endpoint)]', or '#[craft(endpoint(...))]'.", - )); + item_fn, + format!("The attribute macro `{attr_str}` on a method must be filled with sub-attributes, such as '#[craft(handler)]', '#[craft(endpoint)]', or '#[craft(endpoint(...))]'."), + )); } if let Some(index) = index { item_fn.attrs.remove(index); From 56ec5f8a540f76fe195de8bda5aadc8c04ed2f7d Mon Sep 17 00:00:00 2001 From: Andeya Date: Sun, 29 Sep 2024 14:35:13 +0800 Subject: [PATCH 3/6] chore(craft-macros): Add test --- crates/craft-macros/src/craft.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/crates/craft-macros/src/craft.rs b/crates/craft-macros/src/craft.rs index d71781b54..05f292087 100644 --- a/crates/craft-macros/src/craft.rs +++ b/crates/craft-macros/src/craft.rs @@ -165,3 +165,26 @@ fn rewrite_method( *method = new_method; Ok(()) } + +#[cfg(test)] +mod tests { + use super::REGEX_STR; + use regex::Regex; + + #[test] + fn extract_attribute() { + let re = Regex::new(REGEX_STR).unwrap(); + + let texts = vec![ + r###"#[dipper(endpoint(responses((status_code = 400, description = "[(Wrong)] request parameters."))))]"###, + r###"#[dipper(handler())]"###, + r###"#[dipper(endpoint(simple_text))] "###, + r###"#[dipper(handler)]"###, + ]; + for text in texts { + for caps in re.captures_iter(text) { + println!("name={}, content={:?}", caps.name("name").unwrap().as_str(), caps.name("content").map(|c| c.as_str().to_owned())) + } + } + } +} From f4e456c5943d8ae9585305fb9b18aab028eb378f Mon Sep 17 00:00:00 2001 From: Andeya Date: Sun, 29 Sep 2024 14:36:11 +0800 Subject: [PATCH 4/6] chore(craft-macros): fix test --- crates/craft-macros/src/craft.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/crates/craft-macros/src/craft.rs b/crates/craft-macros/src/craft.rs index 05f292087..85575445c 100644 --- a/crates/craft-macros/src/craft.rs +++ b/crates/craft-macros/src/craft.rs @@ -176,14 +176,18 @@ mod tests { let re = Regex::new(REGEX_STR).unwrap(); let texts = vec![ - r###"#[dipper(endpoint(responses((status_code = 400, description = "[(Wrong)] request parameters."))))]"###, - r###"#[dipper(handler())]"###, - r###"#[dipper(endpoint(simple_text))] "###, - r###"#[dipper(handler)]"###, + r###"#[craft(endpoint(responses((status_code = 400, description = "[(Wrong)] request parameters."))))]"###, + r###"#[craft(handler())]"###, + r###"#[craft(endpoint(simple_text))] "###, + r###"#[craft(handler)]"###, ]; for text in texts { for caps in re.captures_iter(text) { - println!("name={}, content={:?}", caps.name("name").unwrap().as_str(), caps.name("content").map(|c| c.as_str().to_owned())) + println!( + "name={}, content={:?}", + caps.name("name").unwrap().as_str(), + caps.name("content").map(|c| c.as_str().to_owned()) + ) } } } From d6e6988fcf0cd1bd8df68107a0bc9241401568c0 Mon Sep 17 00:00:00 2001 From: Andeya Date: Sun, 29 Sep 2024 15:23:54 +0800 Subject: [PATCH 5/6] chore(craft-macros): fix REGEX_STR --- crates/craft-macros/src/craft.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/crates/craft-macros/src/craft.rs b/crates/craft-macros/src/craft.rs index 85575445c..4624c9fe5 100644 --- a/crates/craft-macros/src/craft.rs +++ b/crates/craft-macros/src/craft.rs @@ -29,12 +29,12 @@ pub(crate) fn generate(input: Item) -> syn::Result { } } -const REGEX_STR: &'static str = - r#"(?s)#\s*\[\s*craft\s*\(\s*(?Phandler|endpoint)\s*(?P\(.*\))?\s*\)\s*\]"#; +const REGEX_STR: &'static str = r#"(?s)#\s*\[\s*(::)?\s*([a-zA-z][a-zA-z0-9]*\s*::\s*)*\s*craft\s*\(\s*(?Phandler|endpoint)\s*(?P\(.*\))?\s*\)\s*\]"#; fn take_method_macro(item_fn: &mut ImplItemFn) -> syn::Result> { let mut index: Option = None; let mut new_attr: Option = None; + let re = Regex::new(REGEX_STR).unwrap(); for (idx, attr) in &mut item_fn.attrs.iter().enumerate() { if !(match attr.path().segments.last() { Some(segment) => segment.ident == "craft", @@ -42,7 +42,6 @@ fn take_method_macro(item_fn: &mut ImplItemFn) -> syn::Result> }) { continue; } - let re = Regex::new(REGEX_STR).unwrap(); let attr_str = attr.to_token_stream().to_string().trim().to_owned(); if let Some(caps) = re.captures(&attr_str) { if let Some(name) = caps.name("name") { @@ -176,9 +175,9 @@ mod tests { let re = Regex::new(REGEX_STR).unwrap(); let texts = vec![ - r###"#[craft(endpoint(responses((status_code = 400, description = "[(Wrong)] request parameters."))))]"###, - r###"#[craft(handler())]"###, - r###"#[craft(endpoint(simple_text))] "###, + r###"#[:: craft(endpoint(responses((status_code = 400, description = "[(Wrong)] request parameters."))))]"###, + r###"#[ xx ::craft(handler())]"###, + r###"#[::xx::craft(endpoint(simple_text))] "###, r###"#[craft(handler)]"###, ]; for text in texts { From 7aa26c8e5fc67239e87cab7a1c0112647b5bef6a Mon Sep 17 00:00:00 2001 From: Andeya Date: Sun, 29 Sep 2024 15:35:23 +0800 Subject: [PATCH 6/6] chore(craft-macros): fix REGEX_STR --- crates/craft-macros/src/craft.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/craft-macros/src/craft.rs b/crates/craft-macros/src/craft.rs index 4624c9fe5..dcf1d94d8 100644 --- a/crates/craft-macros/src/craft.rs +++ b/crates/craft-macros/src/craft.rs @@ -29,7 +29,7 @@ pub(crate) fn generate(input: Item) -> syn::Result { } } -const REGEX_STR: &'static str = r#"(?s)#\s*\[\s*(::)?\s*([a-zA-z][a-zA-z0-9]*\s*::\s*)*\s*craft\s*\(\s*(?Phandler|endpoint)\s*(?P\(.*\))?\s*\)\s*\]"#; +const REGEX_STR: &'static str = r#"(?s)#\s*\[\s*(::)?\s*([A-Za-z_][A-Za-z0-9_]*\s*::\s*)*\s*craft\s*\(\s*(?Phandler|endpoint)\s*(?P\(.*\))?\s*\)\s*\]"#; fn take_method_macro(item_fn: &mut ImplItemFn) -> syn::Result> { let mut index: Option = None;