feat(rmcp-macro): generate description from docs (#141)
* feat: generate description from docs * fix logic to extract doc * fmt * chore: undo unnecessary changes in `Cargo.toml` * chore: undo unnecessary changes in `Cargo.toml` * chore: remove unnecessary code in tests * create `extract_doc_line` * fmt * make sure the string is not empty * avoid multilayer nesting * fmt
This commit is contained in:
parent
9d6f9a2e0d
commit
3a97917cd7
1 changed files with 76 additions and 1 deletions
|
|
@ -296,6 +296,35 @@ pub(crate) fn tool_impl_item(attr: TokenStream, mut input: ItemImpl) -> syn::Res
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn extract_doc_line(attr: &syn::Attribute) -> Option<String> {
|
||||||
|
if !attr.path().is_ident("doc") {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let name_value = match &attr.meta {
|
||||||
|
syn::Meta::NameValue(nv) => nv,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let expr_lit = match &name_value.value {
|
||||||
|
syn::Expr::Lit(lit) => lit,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let lit_str = match &expr_lit.lit {
|
||||||
|
syn::Lit::Str(s) => s,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let content = lit_str.value().trim().to_string();
|
||||||
|
|
||||||
|
if content.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn tool_fn_item(attr: TokenStream, mut input_fn: ItemFn) -> syn::Result<TokenStream> {
|
pub(crate) fn tool_fn_item(attr: TokenStream, mut input_fn: ItemFn) -> syn::Result<TokenStream> {
|
||||||
let mut tool_macro_attrs = ToolAttrs::default();
|
let mut tool_macro_attrs = ToolAttrs::default();
|
||||||
let args: ToolFnItemAttrs = syn::parse2(attr)?;
|
let args: ToolFnItemAttrs = syn::parse2(attr)?;
|
||||||
|
|
@ -405,10 +434,19 @@ pub(crate) fn tool_fn_item(attr: TokenStream, mut input_fn: ItemFn) -> syn::Resu
|
||||||
// generate get tool attr function
|
// generate get tool attr function
|
||||||
let tool_attr_fn = {
|
let tool_attr_fn = {
|
||||||
let description = if let Some(expr) = tool_macro_attrs.fn_item.description {
|
let description = if let Some(expr) = tool_macro_attrs.fn_item.description {
|
||||||
|
// Use explicitly provided description if available
|
||||||
expr
|
expr
|
||||||
} else {
|
} else {
|
||||||
|
// Try to extract documentation comments
|
||||||
|
let doc_content = input_fn
|
||||||
|
.attrs
|
||||||
|
.iter()
|
||||||
|
.filter_map(extract_doc_line)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
parse_quote! {
|
parse_quote! {
|
||||||
""
|
#doc_content.trim().to_string()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let schema = match &tool_macro_attrs.params {
|
let schema = match &tool_macro_attrs.params {
|
||||||
|
|
@ -657,4 +695,41 @@ mod test {
|
||||||
println!("input: {:#}", input);
|
println!("input: {:#}", input);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_doc_comment_description() -> syn::Result<()> {
|
||||||
|
let attr = quote! {}; // No explicit description
|
||||||
|
let input = quote! {
|
||||||
|
/// This is a test description from doc comments
|
||||||
|
/// with multiple lines
|
||||||
|
fn test_function(&self) -> Result<(), Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let result = tool(attr, input)?;
|
||||||
|
|
||||||
|
// The output should contain the description from doc comments
|
||||||
|
let result_str = result.to_string();
|
||||||
|
assert!(result_str.contains("This is a test description from doc comments"));
|
||||||
|
assert!(result_str.contains("with multiple lines"));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_explicit_description_priority() -> syn::Result<()> {
|
||||||
|
let attr = quote! {
|
||||||
|
description = "Explicit description has priority"
|
||||||
|
};
|
||||||
|
let input = quote! {
|
||||||
|
/// Doc comment description that should be ignored
|
||||||
|
fn test_function(&self) -> Result<(), Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let result = tool(attr, input)?;
|
||||||
|
|
||||||
|
// The output should contain the explicit description
|
||||||
|
let result_str = result.to_string();
|
||||||
|
assert!(result_str.contains("Explicit description has priority"));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue