@@ -3,7 +3,7 @@ use proc_macro_error::{abort, abort_call_site, proc_macro_error, ResultExt};
3
3
use quote:: quote;
4
4
use syn:: {
5
5
parse_macro_input, parse_quote, punctuated:: Punctuated , DataStruct , DeriveInput , Field , Fields ,
6
- FieldsNamed , Path , Token ,
6
+ FieldsNamed , Lit , Meta , MetaList , MetaNameValue , NestedMeta , Path , Token ,
7
7
} ;
8
8
9
9
// TODO generally should use fully qualified names for trait function calls
@@ -26,27 +26,40 @@ pub fn attribute_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStre
26
26
..
27
27
} = parse_macro_input ! ( input as DeriveInput ) ;
28
28
29
- let attribute_ident: String = attrs
29
+ let mut attribute_ident = None ;
30
+ let mut invalid_field = None ;
31
+
32
+ for attribute in attrs
30
33
. into_iter ( )
31
- . find_map ( |attribute| {
32
- if attribute. path . is_ident ( "attribute" ) {
33
- let path: Path = attribute. parse_args ( ) . unwrap_or_abort ( ) ;
34
- Some (
35
- path. get_ident ( )
36
- . unwrap_or_else ( || {
37
- abort_call_site ! ( "Only single idents are currently supported" )
38
- } )
39
- . to_string ( ) ,
40
- )
41
- } else {
42
- None
34
+ . filter ( |attribute| attribute. path . is_ident ( "attribute" ) )
35
+ {
36
+ const VALID_FORMAT : & str = r#"Expected `#[attribute(ident="name_of_your_attribute", invalid_field="error message")]`"# ;
37
+ let meta: Meta = attribute. parse_meta ( ) . unwrap_or_abort ( ) ;
38
+ if let Meta :: List ( meta) = meta {
39
+ for meta in meta. nested {
40
+ if let NestedMeta :: Meta ( Meta :: NameValue ( MetaNameValue { path, lit, .. } ) ) = meta {
41
+ match (
42
+ path. get_ident ( )
43
+ . unwrap_or_else ( || abort_call_site ! ( VALID_FORMAT ) )
44
+ . to_string ( )
45
+ . as_str ( ) ,
46
+ lit,
47
+ ) {
48
+ ( "ident" , Lit :: Str ( lit) ) => attribute_ident = Some ( lit. value ( ) ) ,
49
+ ( "invalid_field" , Lit :: Str ( lit) ) => invalid_field = Some ( lit. value ( ) ) ,
50
+ _ => abort_call_site ! ( VALID_FORMAT ) ,
51
+ }
52
+ } else {
53
+ abort_call_site ! ( VALID_FORMAT ) ;
54
+ }
43
55
}
44
- } )
45
- . unwrap_or_else ( || {
46
- abort_call_site ! (
47
- "You need to specify the attribute path via `#[attribute(name_of_your_attribute)]`"
48
- ) ;
49
- } ) ;
56
+ }
57
+ }
58
+ let attribute_ident: String = attribute_ident. unwrap_or_else ( || {
59
+ abort_call_site ! (
60
+ r#"You need to specify the attribute path via `#[attribute(ident="name_of_your_attribute")]`"#
61
+ ) ;
62
+ } ) ;
50
63
51
64
let ( impl_generics, ty_generics, where_clause) = generics. split_for_impl ( ) ;
52
65
@@ -66,34 +79,51 @@ pub fn attribute_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStre
66
79
attrs, ident, ty, ..
67
80
} in named. into_iter ( )
68
81
{
69
- let default: bool =
70
- attrs
71
- . into_iter ( )
72
- . find_map ( |attribute| {
73
- if attribute. path . is_ident ( "attribute" ) {
74
- Some (
75
- attribute
76
- . parse_args ( )
77
- . ok ( )
78
- . and_then ( |ident : Ident | {
79
- if ident == "default" {
80
- Some ( true )
81
- } else {
82
- None
83
- }
84
- } )
85
- . unwrap_or_else ( || {
86
- abort ! (
87
- attribute,
88
- "Only `#[attribute(default)]` is currently supported"
89
- )
90
- } ) ,
91
- )
82
+ let mut default = false ;
83
+ let mut missing = None ;
84
+ let mut expected = None ;
85
+ for attribute in attrs
86
+ . into_iter ( )
87
+ . filter ( |attribute| attribute. path . is_ident ( "attribute" ) )
88
+ {
89
+ const VALID_FORMAT : & str = r#"Expected `#[attribute(default, missing="error message", expected="error message"])`"# ;
90
+ let meta: Meta = attribute. parse_meta ( ) . unwrap_or_abort ( ) ;
91
+ match meta {
92
+ Meta :: List ( meta) => {
93
+ for meta in meta. nested {
94
+ if let NestedMeta :: Meta ( Meta :: NameValue ( MetaNameValue {
95
+ path,
96
+ lit,
97
+ ..
98
+ } ) ) = meta
99
+ {
100
+ match (
101
+ path. get_ident ( )
102
+ . unwrap_or_else ( || abort_call_site ! ( VALID_FORMAT ) )
103
+ . to_string ( )
104
+ . as_str ( ) ,
105
+ lit,
106
+ ) {
107
+ ( "missing" , Lit :: Str ( lit) ) => missing = Some ( lit. value ( ) ) ,
108
+ ( "expected" , Lit :: Str ( lit) ) => expected = Some ( lit. value ( ) ) ,
109
+ _ => abort_call_site ! ( VALID_FORMAT ) ,
110
+ }
111
+ } else {
112
+ abort_call_site ! ( VALID_FORMAT ) ;
113
+ }
114
+ }
115
+ }
116
+ Meta :: Path ( path) => {
117
+ if path. is_ident ( "default" ) {
118
+ default = true ;
92
119
} else {
93
- None
120
+ abort_call_site ! ( VALID_FORMAT ) ;
94
121
}
95
- } )
96
- . unwrap_or_default ( ) ;
122
+ }
123
+ _ => ( ) ,
124
+ }
125
+ }
126
+
97
127
let ident = ident. expect ( "named struct fields have idents" ) ;
98
128
let ident_str = ident. to_string ( ) ;
99
129
@@ -108,7 +138,7 @@ pub fn attribute_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStre
108
138
( #none, __value @ #some( _) ) => self . #ident = __value,
109
139
( #some( __first) , #some( __second) ) => {
110
140
let mut __error = <<#ty as :: attribute_derive:: ConvertParsed >:: Type as :: attribute_derive:: Error >:: error( __first, #error1) ;
111
- __error . combine( <<#ty as :: attribute_derive:: ConvertParsed >:: Type as :: attribute_derive:: Error >:: error(
141
+ #syn :: Error :: combine( & mut __error , <<#ty as :: attribute_derive:: ConvertParsed >:: Type as :: attribute_derive:: Error >:: error(
112
142
& __second,
113
143
#error2,
114
144
) ) ;
@@ -118,17 +148,23 @@ pub fn attribute_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStre
118
148
}
119
149
} ) ;
120
150
151
+ let error = if let Some ( expected) = expected {
152
+ quote ! { . map_err( |__error| #syn:: Error :: new( __error. span( ) , #expected) ) }
153
+ } else {
154
+ quote ! ( )
155
+ } ;
156
+
121
157
parsing. push ( quote ! {
122
158
#ident_str => {
123
159
__options. #ident = #some(
124
- // ::attribute_derive::ConvertParsed::convert(__input.parse()?)?
125
- // TODO FQN
126
- __input. parse( ) ?
160
+ #syn:: parse:: Parse :: parse( __input) #error?
127
161
) ;
128
162
}
129
163
} ) ;
130
164
131
- let error = format ! ( "Mandatory `{ident}` was not specified via the attributes." ) ;
165
+ let error = missing. unwrap_or_else ( || {
166
+ format ! ( "Mandatory `{ident}` was not specified via the attributes." )
167
+ } ) ;
132
168
assignments. push ( if default {
133
169
quote ! {
134
170
#ident: __options. #ident. map( |t| :: attribute_derive:: ConvertParsed :: convert( t) ) . unwrap_or_else( || #ok( <#ty as core:: default :: Default >:: default ( ) ) ) ?
@@ -149,18 +185,21 @@ pub fn attribute_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStre
149
185
_ => abort_call_site ! ( "Only works on structs with named fields" ) ,
150
186
} ;
151
187
152
- let error_invalid_name = if possible_variables. len ( ) > 1 {
153
- let last = possible_variables. pop ( ) . unwrap ( ) ;
154
- format ! (
155
- "Supported fields are {} and {}" ,
156
- possible_variables. join( ", " ) ,
157
- last
158
- )
159
- } else {
160
- format ! ( "Expected supported field {}" , possible_variables[ 0 ] )
161
- } ;
188
+ let error_invalid_name = invalid_field. unwrap_or_else ( || {
189
+ if possible_variables. len ( ) > 1 {
190
+ let last = possible_variables. pop ( ) . unwrap ( ) ;
191
+ format ! (
192
+ "Supported fields are {} and {}" ,
193
+ possible_variables. join( ", " ) ,
194
+ last
195
+ )
196
+ } else {
197
+ format ! ( "Expected supported field {}" , possible_variables[ 0 ] )
198
+ }
199
+ } ) ;
162
200
163
201
quote ! {
202
+ #[ allow( unreachable_code) ]
164
203
impl #impl_generics :: attribute_derive:: Attribute for #ident #ty_generics #where_clause {
165
204
fn from_attributes( __attrs: impl :: core:: iter:: IntoIterator <Item = #syn:: Attribute >) -> #syn:: Result <Self >{
166
205
struct __Options{
0 commit comments