|
| 1 | +use proc_macro2::TokenStream; |
| 2 | +use quote::{quote, ToTokens}; |
| 3 | +use syn::{Expr, ItemFn, Stmt}; |
| 4 | + |
| 5 | +/// 対象: 関数 |
| 6 | +/// 動作: 文脈付き呼び出しを行うための前準備を生成 & 文脈付き呼び出しへの変換 |
| 7 | +pub fn proc_macro_impl(args: TokenStream, ast: ItemFn) -> TokenStream { |
| 8 | + let target_trait = args; |
| 9 | + |
| 10 | + let stmts = &ast |
| 11 | + .block |
| 12 | + .stmts |
| 13 | + .iter() |
| 14 | + .map(append_ctx_if_calling) |
| 15 | + .collect::<Vec<_>>(); |
| 16 | + |
| 17 | + quote! { |
| 18 | + // 前準備 |
| 19 | + #[allow(non_local_definitions)] |
| 20 | + impl #target_trait for Ctx {} |
| 21 | + |
| 22 | + // 関数本体 |
| 23 | + #(#stmts);* |
| 24 | + } |
| 25 | +} |
| 26 | + |
| 27 | +fn append_ctx_if_calling(stmt: &Stmt) -> TokenStream { |
| 28 | + let append_ctx = |expr: &Expr| { |
| 29 | + if let Expr::Call(call) = expr { |
| 30 | + let func = call.func.to_token_stream(); |
| 31 | + let args = call.args.to_token_stream(); |
| 32 | + quote! { #func (Ctx, #args) } |
| 33 | + } else if let Expr::MethodCall(call) = expr { |
| 34 | + let receiver = call.receiver.to_token_stream(); |
| 35 | + let method = call.method.to_token_stream(); |
| 36 | + let args = call.args.to_token_stream(); |
| 37 | + quote! { #receiver . #method (Ctx, #args) } |
| 38 | + } else { |
| 39 | + stmt.to_token_stream() |
| 40 | + } |
| 41 | + }; |
| 42 | + |
| 43 | + match stmt { |
| 44 | + Stmt::Expr(expr, _) => append_ctx(expr), |
| 45 | + _ => stmt.to_token_stream(), |
| 46 | + } |
| 47 | +} |
0 commit comments