-
Notifications
You must be signed in to change notification settings - Fork 502
Expand file tree
/
Copy pathcommon.rs
More file actions
98 lines (88 loc) · 3.1 KB
/
common.rs
File metadata and controls
98 lines (88 loc) · 3.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
//! 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<Expr> {
syn::parse2::<Expr>(quote! { None })
}
/// Extract documentation from doc attributes
pub fn extract_doc_line(
existing_docs: Option<Expr>,
attr: &Attribute,
) -> syn::Result<Option<Expr>> {
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<Expr> = 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::<Expr>(quote! { concat!(#existing, "\n", #this) }).map(Some)
}
(Some(existing), None) => Ok(Some(existing)),
(None, Some(this)) => Ok(Some(this)),
_ => Ok(None),
}
}
/// Find Parameters<T> type in function signature
/// Returns the full Parameters<T> type if found
pub fn find_parameters_type_in_sig(sig: &Signature) -> Option<Box<Type>> {
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<T> type in ImplItemFn
pub fn find_parameters_type_impl(fn_item: &ImplItemFn) -> Option<Box<Type>> {
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)
})
}