//! Common utilities shared between different macro implementations use quote::quote; use syn::{Attribute, Expr, FnArg, ImplItem, ImplItemFn, ItemImpl, Signature, Type}; /// Parse a None expression pub fn none_expr() -> syn::Result { syn::parse2::(quote! { None }) } /// Extract documentation from doc attributes pub fn extract_doc_line( existing_docs: Option, attr: &Attribute, ) -> syn::Result> { if !attr.path().is_ident("doc") { return Ok(None); } let syn::Meta::NameValue(name_value) = &attr.meta else { return Ok(None); }; let value = &name_value.value; let this_expr: Option = match value { // Preserve macros such as `include_str!(...)` syn::Expr::Macro(_) => Some(value.clone()), syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(lit_str), .. }) => { let content = lit_str.value().trim().to_string(); if content.is_empty() { return Ok(existing_docs); } Some(Expr::Lit(syn::ExprLit { attrs: Vec::new(), lit: syn::Lit::Str(syn::LitStr::new(&content, lit_str.span())), })) } _ => return Ok(None), }; match (existing_docs, this_expr) { (Some(existing), Some(this)) => { syn::parse2::(quote! { concat!(#existing, "\n", #this) }).map(Some) } (Some(existing), None) => Ok(Some(existing)), (None, Some(this)) => Ok(Some(this)), _ => Ok(None), } } /// Find Parameters type in function signature /// Returns the full Parameters type if found pub fn find_parameters_type_in_sig(sig: &Signature) -> Option> { sig.inputs.iter().find_map(|input| { if let FnArg::Typed(pat_type) = input { if let Type::Path(type_path) = &*pat_type.ty { if type_path .path .segments .last() .is_some_and(|type_name| type_name.ident == "Parameters") { return Some(pat_type.ty.clone()); } } } None }) } /// Find Parameters type in ImplItemFn pub fn find_parameters_type_impl(fn_item: &ImplItemFn) -> Option> { find_parameters_type_in_sig(&fn_item.sig) } /// Check whether an `impl` block already contains a method with the given name. pub fn has_method(name: &str, item_impl: &ItemImpl) -> bool { item_impl.items.iter().any(|item| match item { ImplItem::Fn(func) => func.sig.ident == name, _ => false, }) } /// Check whether an `impl` block carries a sibling handler attribute (e.g. /// `#[prompt_handler]` visible from within `#[tool_handler]`). /// /// Matches both bare (`prompt_handler`) and path-qualified (`rmcp::prompt_handler`) forms. pub fn has_sibling_handler(item_impl: &ItemImpl, handler_name: &str) -> bool { item_impl.attrs.iter().any(|attr| { attr.path() .segments .last() .is_some_and(|seg| seg.ident == handler_name) }) }