semaphore_rs_depth_macros/
lib.rs1use proc_macro::TokenStream;
2use quote::{format_ident, quote};
3use semaphore_rs_depth_config::get_supported_depths;
4use syn::{
5 parse::{Parse, ParseStream},
6 parse_macro_input, parse_quote,
7 visit_mut::VisitMut,
8 Ident, Token,
9};
10
11#[proc_macro_attribute]
43pub fn test_all_depths(_attr: TokenStream, item: TokenStream) -> TokenStream {
44 let fun = parse_macro_input!(item as syn::ItemFn);
45 let fun_name = &fun.sig.ident;
46
47 let original_fun = quote! { #fun };
48 let mut result = TokenStream::from(original_fun);
49
50 for depth in get_supported_depths() {
51 let fun_name_versioned = format_ident!("{}_depth_{}", fun_name, depth);
52 let tokens = quote! {
53 #[test]
54 fn #fun_name_versioned() {
55 #fun_name(#depth);
56 }
57 };
58 result.extend(TokenStream::from(tokens));
59 }
60 result
61}
62
63#[derive(Debug)]
64struct ArrayForDepthsInput {
65 replaced_ident: Ident,
66 expr: syn::Expr,
67}
68
69#[derive(Debug)]
70struct MacroArgs {
71 args: Vec<syn::Expr>,
72}
73
74impl Parse for MacroArgs {
75 fn parse(input: ParseStream) -> syn::Result<Self> {
76 let mut args = Vec::new();
77 while !input.is_empty() {
78 args.push(input.parse::<syn::Expr>()?);
79 if input.is_empty() {
80 break;
81 }
82 input.parse::<Token![,]>()?;
83 }
84 Ok(MacroArgs { args })
85 }
86}
87
88impl MacroArgs {
89 fn tokens(&self) -> proc_macro2::TokenStream {
90 let args = &self.args;
91 quote! { #(#args),* }
92 }
93}
94
95struct IdentReplacer(Ident, syn::Expr);
96
97impl VisitMut for IdentReplacer {
98 fn visit_expr_mut(&mut self, expr: &mut syn::Expr) {
99 match expr {
100 syn::Expr::Path(ident) => {
101 if ident.path.is_ident(&self.0) {
102 *expr = self.1.clone();
103 }
104 }
105 syn::Expr::Macro(mcr) => {
106 let Ok(mut args) = mcr.mac.parse_body::<MacroArgs>() else {
107 return;
108 };
109 for arg in &mut args.args {
110 self.visit_expr_mut(arg);
111 }
112 mcr.mac.tokens = args.tokens();
113 }
114 _ => syn::visit_mut::visit_expr_mut(self, expr),
115 }
116 }
117}
118
119impl Parse for ArrayForDepthsInput {
120 fn parse(input: ParseStream) -> syn::Result<Self> {
121 input.parse::<Token![|]>()?;
122 let replaced_ident = input.parse::<Ident>()?;
123 input.parse::<Token![|]>()?;
124 let expr = input.parse::<syn::Expr>()?;
125 Ok(ArrayForDepthsInput {
126 replaced_ident,
127 expr,
128 })
129 }
130}
131
132#[proc_macro]
147pub fn array_for_depths(input: TokenStream) -> TokenStream {
148 let input = parse_macro_input!(input as ArrayForDepthsInput);
149 let items = get_supported_depths()
150 .iter()
151 .map(|depth| {
152 let mut replacer = IdentReplacer(input.replaced_ident.clone(), parse_quote!(#depth));
153 let mut expr = input.expr.clone();
154 replacer.visit_expr_mut(&mut expr);
155 expr
156 })
157 .collect::<Vec<_>>();
158 let array = quote! { [#(#items),*] };
159 array.into()
160}