use proc_macro2::TokenStream;
use quote::quote;
use syn::{parse::Parse, parse::ParseStream, Error, Expr, ItemFn, LitStr, Result, Token};
#[derive(Debug)]
pub struct AdviceArgs {
pub pointcut: String,
pub advice_type: Option<String>,
pub order: i32,
}
impl Parse for AdviceArgs {
fn parse(input: ParseStream) -> Result<Self> {
let mut pointcut = None;
let mut advice_type = None;
let mut order = 0;
while !input.is_empty() {
let key: syn::Ident = input.parse()?;
input.parse::<Token![=]>()?;
match key.to_string().as_str() {
"pointcut" => {
let value: LitStr = input.parse()?;
pointcut = Some(value.value());
}
"advice" => {
let value: LitStr = input.parse()?;
advice_type = Some(value.value());
}
"order" => {
let value: Expr = input.parse()?;
if let Expr::Lit(expr_lit) = value {
if let syn::Lit::Int(lit_int) = expr_lit.lit {
order = lit_int.base10_parse()?;
}
}
}
_ => {
return Err(Error::new(
key.span(),
format!("Unknown attribute key: {}", key),
))
}
}
if input.peek(Token![,]) {
input.parse::<Token![,]>()?;
}
}
let pointcut = pointcut.ok_or_else(|| {
Error::new(input.span(), "Missing required attribute: pointcut")
})?;
Ok(AdviceArgs {
pointcut,
advice_type,
order,
})
}
}
pub fn transform(args: AdviceArgs, func: ItemFn) -> Result<TokenStream> {
let func_name = &func.sig.ident;
let aspect_struct_name = quote::format_ident!("{}Aspect", func_name);
let registrar_name = quote::format_ident!("__register_{}", func_name);
let pointcut_str = &args.pointcut;
let order = args.order;
let advice_impl = match args.advice_type.as_deref() {
Some("before") => generate_before_impl(&func)?,
Some("after") => generate_after_impl(&func)?,
Some("after_error") => generate_after_error_impl(&func)?,
Some("around") | None => generate_around_impl(&func)?,
Some(other) => {
return Err(Error::new_spanned(
&func.sig.ident,
format!("Invalid advice type: {}. Must be one of: before, after, after_error, around", other),
))
}
};
let output = quote! {
#func
#[derive(Clone, Copy)]
struct #aspect_struct_name;
impl aspect_core::Aspect for #aspect_struct_name {
#advice_impl
}
#[allow(non_upper_case_globals)]
static #registrar_name: aspect_runtime::once_cell::sync::Lazy<()> =
aspect_runtime::once_cell::sync::Lazy::new(|| {
let pointcut = aspect_core::pointcut::Pointcut::parse(#pointcut_str)
.expect(&format!("Invalid pointcut expression: {}", #pointcut_str));
aspect_runtime::global_registry().register(
std::sync::Arc::new(#aspect_struct_name),
pointcut,
#order,
Some(stringify!(#func_name).to_string()),
);
});
const _: () = {
let _ = &#registrar_name as &aspect_runtime::once_cell::sync::Lazy<()>;
};
};
Ok(output)
}
fn generate_before_impl(func: &ItemFn) -> Result<TokenStream> {
let func_name = &func.sig.ident;
Ok(quote! {
fn before(&self, ctx: &aspect_core::JoinPoint) {
#func_name(ctx);
}
})
}
fn generate_after_impl(func: &ItemFn) -> Result<TokenStream> {
let func_name = &func.sig.ident;
Ok(quote! {
fn after(&self, ctx: &aspect_core::JoinPoint, result: &dyn std::any::Any) {
#func_name(ctx, result);
}
})
}
fn generate_after_error_impl(func: &ItemFn) -> Result<TokenStream> {
let func_name = &func.sig.ident;
Ok(quote! {
fn after_error(&self, ctx: &aspect_core::JoinPoint, error: &aspect_core::AspectError) {
#func_name(ctx, error);
}
})
}
fn generate_around_impl(func: &ItemFn) -> Result<TokenStream> {
let func_name = &func.sig.ident;
Ok(quote! {
fn around(
&self,
pjp: aspect_core::ProceedingJoinPoint
) -> Result<Box<dyn std::any::Any>, aspect_core::AspectError> {
#func_name(pjp)
}
})
}