1use proc_macro::TokenStream;
2use quote::{quote, format_ident};
3use syn::{parse_macro_input, ItemFn, LitStr};
4
5const fn hash_str(s: &str) -> u64 {
7 let mut hash = 0xcbf29ce484222325;
8 let bytes = s.as_bytes();
9 let mut i = 0;
10 while i < bytes.len() {
11 hash ^= bytes[i] as u64;
12 hash = hash.wrapping_mul(0x100000001b3);
13 i += 1;
14 }
15 hash
16}
17
18#[proc_macro_attribute]
21pub fn node(attr: TokenStream, item: TokenStream) -> TokenStream {
22 let name_lit = parse_macro_input!(attr as LitStr);
23 let name_str = name_lit.value();
24 let hash = hash_str(&name_str);
25 let tag_ident = format_ident!("__pipeline_tag_{}", hash);
26
27 let input_fn = parse_macro_input!(item as ItemFn);
28 let fn_name = &input_fn.sig.ident;
29 let fn_vis = &input_fn.vis;
30
31 let args = &input_fn.sig.inputs;
33 let mut arg_types = Vec::new();
34 let mut arg_names = Vec::new();
35 for (i, arg) in args.iter().enumerate() {
36 if let syn::FnArg::Typed(pat_type) = arg {
37 arg_types.push(pat_type.ty.clone());
38 arg_names.push(format_ident!("arg_{}", i));
39 }
40 }
41
42 let ret_type = match &input_fn.sig.output {
43 syn::ReturnType::Default => quote! { () },
44 syn::ReturnType::Type(_, ty) => quote! { #ty },
45 };
46
47 let expanded = quote! {
48 #input_fn
49
50 #[allow(non_camel_case_types)]
51 #[derive(Clone, Copy)]
52 #fn_vis struct #tag_ident;
53
54 impl<I> pipe_it::handler::Handler<I, #ret_type, (#(#arg_types),*)>
55 for #tag_ident
56 where
57 I: Clone + Send + Sync + 'static,
58 #ret_type: Send + 'static,
59 #(
60 #arg_types: pipe_it::FromContext<I>,
61 )*
62 {
63 fn call(&self, ctx: pipe_it::Context<I>) -> impl std::future::Future<Output = #ret_type> + Send {
64 async move {
65 let args = {
66 let c = ctx;
67 (
68 #(
69 <#arg_types as pipe_it::FromContext<I>>::from(c.clone()).await
70 ),*
71 )
72 };
73 let (#(#arg_names),*) = args;
74 #fn_name(
75 #(#arg_names),*
76 ).await
77 }
78 }
79 }
80 };
81
82 TokenStream::from(expanded)
83}
84
85#[proc_macro]
88pub fn tag_struct(input: TokenStream) -> TokenStream {
89 let lit = parse_macro_input!(input as LitStr);
90 let hash = hash_str(&lit.value());
91 let ident = format_ident!("__pipeline_tag_{}", hash);
92 quote! { #ident }.into()
93}
94
95#[proc_macro]
97pub fn tag_id(input: TokenStream) -> TokenStream {
98 let lit = parse_macro_input!(input as LitStr);
99 let hash = hash_str(&lit.value());
100 quote! { #hash }.into()
101}