@@ -24,6 +24,20 @@ struct McpToolMacroAttributes {
24
24
description : Option < String > ,
25
25
}
26
26
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
+
27
41
impl Parse for McpToolMacroAttributes {
28
42
/// Parses the macro attributes from a `ParseStream`.
29
43
///
@@ -41,51 +55,77 @@ impl Parse for McpToolMacroAttributes {
41
55
for meta in meta_list {
42
56
if let Meta :: NameValue ( meta_name_value) = meta {
43
57
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
+ ) ) ;
53
101
}
102
+ } ;
103
+
104
+ match ident_str. as_str ( ) {
105
+ "name" => name = Some ( value) ,
106
+ "description" => description = Some ( value) ,
107
+ _ => { }
54
108
}
55
109
}
56
110
}
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
+ ) ) ;
72
118
}
73
119
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
+ ) ) ;
89
129
}
90
130
91
131
Ok ( Self { name, description } )
@@ -360,7 +400,7 @@ mod tests {
360
400
assert ! ( result. is_err( ) ) ;
361
401
assert_eq ! (
362
402
result. err( ) . unwrap( ) . to_string( ) ,
363
- "The 'name' attribute is required."
403
+ "The 'name' attribute is required and must not be empty ."
364
404
)
365
405
}
366
406
@@ -371,7 +411,7 @@ mod tests {
371
411
assert ! ( result. is_err( ) ) ;
372
412
assert_eq ! (
373
413
result. err( ) . unwrap( ) . to_string( ) ,
374
- "The 'description' attribute is required."
414
+ "The 'description' attribute is required and must not be empty ."
375
415
)
376
416
}
377
417
@@ -382,7 +422,7 @@ mod tests {
382
422
assert ! ( result. is_err( ) ) ;
383
423
assert_eq ! (
384
424
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."
386
426
) ;
387
427
}
388
428
#[ test]
@@ -392,7 +432,7 @@ mod tests {
392
432
assert ! ( result. is_err( ) ) ;
393
433
assert_eq ! (
394
434
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."
396
436
) ;
397
437
}
398
438
}
0 commit comments