use quote::{format_ident, quote};
use syn::{Ident, ItemFn};
pub fn extract_f_and_ctx_types(generics: &syn::Generics) -> (syn::Ident, syn::Ident) {
let mut type_params = generics.params.iter().filter_map(|param| {
if let syn::GenericParam::Type(type_param) = param {
Some(&type_param.ident)
} else {
None
}
});
let f_type = type_params
.next()
.expect("Function must have at least one type parameter (F)")
.clone();
let ctx_type = type_params
.next()
.expect("Function must have at least two type parameters (F and CTX)")
.clone();
(f_type, ctx_type)
}
pub fn build_generic_args(generics: &syn::Generics) -> Vec<proc_macro2::TokenStream> {
generics
.params
.iter()
.map(|param| match param {
syn::GenericParam::Type(type_param) => {
let ident = &type_param.ident;
quote! { #ident }
}
syn::GenericParam::Lifetime(lifetime) => {
let lifetime = &lifetime.lifetime;
quote! { #lifetime }
}
syn::GenericParam::Const(const_param) => {
let ident = &const_param.ident;
quote! { #ident }
}
})
.collect()
}
pub fn handler_name_from_fn(fn_name: &Ident) -> Ident {
let new_name_str = fn_name
.to_string()
.strip_suffix("_impl")
.map(|base| format!("{base}_handler"))
.unwrap_or_else(|| format!("{fn_name}_handler"));
format_ident!("{}", new_name_str)
}
pub fn returns_result_type(input_fn: &ItemFn) -> bool {
match &input_fn.sig.output {
syn::ReturnType::Type(_, ty) => {
matches!(**ty, syn::Type::Path(ref path) if path.path.segments.last().is_some_and(|seg| seg.ident == "Result"))
}
_ => false,
}
}