@@ -24,6 +24,20 @@ struct McpToolMacroAttributes {
2424 description : Option < String > ,
2525}
2626
27+ use syn:: parse:: ParseStream ;
28+
29+ struct ExprList {
30+ exprs : Punctuated < Expr , Token ! [ , ] > ,
31+ }
32+
33+ impl Parse for ExprList {
34+ fn parse ( input : ParseStream ) -> syn:: Result < Self > {
35+ Ok ( ExprList {
36+ exprs : Punctuated :: parse_terminated ( input) ?,
37+ } )
38+ }
39+ }
40+
2741impl Parse for McpToolMacroAttributes {
2842 /// Parses the macro attributes from a `ParseStream`.
2943 ///
@@ -41,51 +55,77 @@ impl Parse for McpToolMacroAttributes {
4155 for meta in meta_list {
4256 if let Meta :: NameValue ( meta_name_value) = meta {
4357 let ident = meta_name_value. path . get_ident ( ) . unwrap ( ) ;
44- if let Expr :: Lit ( ExprLit {
45- lit : Lit :: Str ( lit_str) ,
46- ..
47- } ) = meta_name_value. value
48- {
49- match ident. to_string ( ) . as_str ( ) {
50- "name" => name = Some ( lit_str. value ( ) ) ,
51- "description" => description = Some ( lit_str. value ( ) ) ,
52- _ => { }
58+ let ident_str = ident. to_string ( ) ;
59+
60+ let value = match & meta_name_value. value {
61+ Expr :: Lit ( ExprLit {
62+ lit : Lit :: Str ( lit_str) ,
63+ ..
64+ } ) => lit_str. value ( ) ,
65+
66+ Expr :: Macro ( expr_macro) => {
67+ let mac = & expr_macro. mac ;
68+ if mac. path . is_ident ( "concat" ) {
69+ let args: ExprList = syn:: parse2 ( mac. tokens . clone ( ) ) ?;
70+ let mut result = String :: new ( ) ;
71+
72+ for expr in args. exprs {
73+ if let Expr :: Lit ( ExprLit {
74+ lit : Lit :: Str ( lit_str) ,
75+ ..
76+ } ) = expr
77+ {
78+ result. push_str ( & lit_str. value ( ) ) ;
79+ } else {
80+ return Err ( Error :: new_spanned (
81+ expr,
82+ "Only string literals are allowed inside concat!()" ,
83+ ) ) ;
84+ }
85+ }
86+
87+ result
88+ } else {
89+ return Err ( Error :: new_spanned (
90+ expr_macro,
91+ "Only concat!(...) is supported here" ,
92+ ) ) ;
93+ }
94+ }
95+
96+ _ => {
97+ return Err ( Error :: new_spanned (
98+ & meta_name_value. value ,
99+ "Expected a string literal or concat!(...)" ,
100+ ) ) ;
53101 }
102+ } ;
103+
104+ match ident_str. as_str ( ) {
105+ "name" => name = Some ( value) ,
106+ "description" => description = Some ( value) ,
107+ _ => { }
54108 }
55109 }
56110 }
57- match & name {
58- Some ( tool_name) => {
59- if tool_name. trim ( ) . is_empty ( ) {
60- return Err ( Error :: new (
61- attributes. span ( ) ,
62- "The 'name' attribute should not be an empty string." ,
63- ) ) ;
64- }
65- }
66- None => {
67- return Err ( Error :: new (
68- attributes. span ( ) ,
69- "The 'name' attribute is required." ,
70- ) ) ;
71- }
111+
112+ // Validate presence and non-emptiness
113+ if name. as_ref ( ) . map ( |s| s. trim ( ) . is_empty ( ) ) . unwrap_or ( true ) {
114+ return Err ( Error :: new (
115+ attributes. span ( ) ,
116+ "The 'name' attribute is required and must not be empty." ,
117+ ) ) ;
72118 }
73119
74- match & description {
75- Some ( description) => {
76- if description. trim ( ) . is_empty ( ) {
77- return Err ( Error :: new (
78- attributes. span ( ) ,
79- "The 'description' attribute should not be an empty string." ,
80- ) ) ;
81- }
82- }
83- None => {
84- return Err ( Error :: new (
85- attributes. span ( ) ,
86- "The 'description' attribute is required." ,
87- ) ) ;
88- }
120+ if description
121+ . as_ref ( )
122+ . map ( |s| s. trim ( ) . is_empty ( ) )
123+ . unwrap_or ( true )
124+ {
125+ return Err ( Error :: new (
126+ attributes. span ( ) ,
127+ "The 'description' attribute is required and must not be empty." ,
128+ ) ) ;
89129 }
90130
91131 Ok ( Self { name, description } )
@@ -360,7 +400,7 @@ mod tests {
360400 assert ! ( result. is_err( ) ) ;
361401 assert_eq ! (
362402 result. err( ) . unwrap( ) . to_string( ) ,
363- "The 'name' attribute is required."
403+ "The 'name' attribute is required and must not be empty ."
364404 )
365405 }
366406
@@ -371,7 +411,7 @@ mod tests {
371411 assert ! ( result. is_err( ) ) ;
372412 assert_eq ! (
373413 result. err( ) . unwrap( ) . to_string( ) ,
374- "The 'description' attribute is required."
414+ "The 'description' attribute is required and must not be empty ."
375415 )
376416 }
377417
@@ -382,7 +422,7 @@ mod tests {
382422 assert ! ( result. is_err( ) ) ;
383423 assert_eq ! (
384424 result. err( ) . unwrap( ) . to_string( ) ,
385- "The 'name' attribute should not be an empty string ."
425+ "The 'name' attribute is required and must not be empty."
386426 ) ;
387427 }
388428 #[ test]
@@ -392,7 +432,7 @@ mod tests {
392432 assert ! ( result. is_err( ) ) ;
393433 assert_eq ! (
394434 result. err( ) . unwrap( ) . to_string( ) ,
395- "The 'description' attribute should not be an empty string ."
435+ "The 'description' attribute is required and must not be empty."
396436 ) ;
397437 }
398438}
0 commit comments