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 for arg in args {
35 if let syn::FnArg::Typed(pat_type) = arg {
36 arg_types.push(pat_type.ty.clone());
37 }
38 }
39
40 let ret_type = match &input_fn.sig.output {
41 syn::ReturnType::Default => quote! { () },
42 syn::ReturnType::Type(_, ty) => quote! { #ty },
43 };
44
45 let expanded = quote! {
46 #input_fn
47
48 #[allow(non_camel_case_types)]
49 #[derive(Clone, Copy)]
50 #fn_vis struct #tag_ident;
51
52 impl<I> pipe_it::handler::Handler<I, #ret_type, (#(#arg_types),*)>
53 for #tag_ident
54 where
55 I: Clone + Send + Sync + 'static,
56 #ret_type: Send + 'static,
57 #(
58 #arg_types: pipe_it::FromContext<I>,
59 )*
60 {
61 fn call(&self, ctx: pipe_it::Context<I>) -> impl std::future::Future<Output = #ret_type> + Send {
62 async move {
63 #fn_name(
64 #(
65 <#arg_types as pipe_it::FromContext<I>>::from(ctx.clone()).await
66 ),*
67 ).await
68 }
69 }
70 }
71 };
72
73 TokenStream::from(expanded)
74}
75
76#[proc_macro]
79pub fn tag_struct(input: TokenStream) -> TokenStream {
80 let lit = parse_macro_input!(input as LitStr);
81 let hash = hash_str(&lit.value());
82 let ident = format_ident!("__pipeline_tag_{}", hash);
83 quote! { #ident }.into()
84}
85
86#[proc_macro]
88pub fn tag_id(input: TokenStream) -> TokenStream {
89 let lit = parse_macro_input!(input as LitStr);
90 let hash = hash_str(&lit.value());
91 quote! { #hash }.into()
92}