|
1 | 1 | use darling::{FromMeta, ast::NestedMeta}; |
2 | | -use proc_macro2::TokenStream; |
| 2 | +use proc_macro2::{Span, TokenStream}; |
3 | 3 | use quote::{ToTokens, format_ident, quote}; |
4 | | -use syn::{Expr, Ident, ImplItemFn, ReturnType, parse_quote}; |
| 4 | +use syn::{Expr, Ident, ImplItemFn, LitStr, ReturnType, parse_quote}; |
5 | 5 |
|
6 | 6 | use crate::common::{extract_doc_line, none_expr}; |
7 | 7 |
|
@@ -82,7 +82,7 @@ pub struct ToolAttribute { |
82 | 82 | pub struct ResolvedToolAttribute { |
83 | 83 | pub name: String, |
84 | 84 | pub title: Option<String>, |
85 | | - pub description: Option<String>, |
| 85 | + pub description: Option<Expr>, |
86 | 86 | pub input_schema: Expr, |
87 | 87 | pub output_schema: Option<Expr>, |
88 | 88 | pub annotations: Expr, |
@@ -244,11 +244,17 @@ pub fn tool(attr: TokenStream, input: TokenStream) -> syn::Result<TokenStream> { |
244 | 244 | } |
245 | 245 | }); |
246 | 246 |
|
| 247 | + let description_expr = if let Some(s) = attribute.description { |
| 248 | + Some(Expr::Lit(syn::ExprLit { |
| 249 | + attrs: Vec::new(), |
| 250 | + lit: syn::Lit::Str(LitStr::new(&s, Span::call_site())), |
| 251 | + })) |
| 252 | + } else { |
| 253 | + fn_item.attrs.iter().try_fold(None, extract_doc_line)? |
| 254 | + }; |
247 | 255 | let resolved_tool_attr = ResolvedToolAttribute { |
248 | 256 | name: attribute.name.unwrap_or_else(|| fn_ident.to_string()), |
249 | | - description: attribute |
250 | | - .description |
251 | | - .or_else(|| fn_item.attrs.iter().fold(None, extract_doc_line)), |
| 257 | + description: description_expr, |
252 | 258 | input_schema: input_schema_expr, |
253 | 259 | output_schema: output_schema_expr, |
254 | 260 | annotations: annotations_expr, |
@@ -352,4 +358,22 @@ mod test { |
352 | 358 | assert!(result_str.contains("Explicit description has priority")); |
353 | 359 | Ok(()) |
354 | 360 | } |
| 361 | + |
| 362 | + #[test] |
| 363 | + fn test_doc_include_description() -> syn::Result<()> { |
| 364 | + let attr = quote! {}; // No explicit description |
| 365 | + let input = quote! { |
| 366 | + #[doc = include_str!("some/test/data/doc.txt")] |
| 367 | + fn test_function(&self) -> Result<(), Error> { |
| 368 | + Ok(()) |
| 369 | + } |
| 370 | + }; |
| 371 | + let result = tool(attr, input)?; |
| 372 | + |
| 373 | + // The macro should preserve include_str! in the generated tokens so we at least |
| 374 | + // see the include_str invocation in the generated function source. |
| 375 | + let result_str = result.to_string(); |
| 376 | + assert!(result_str.contains("include_str")); |
| 377 | + Ok(()) |
| 378 | + } |
355 | 379 | } |
0 commit comments