1
+ use proc_macro2:: Span ;
2
+ use std:: fmt:: Debug ;
1
3
use syn:: spanned:: Spanned ;
2
4
3
5
#[ derive( Debug ) ]
@@ -46,131 +48,86 @@ impl PropertyAttrArgsBuilder {
46
48
}
47
49
}
48
50
51
+ /// Error returned when a value are set twice
52
+ /// e.g. #[property(set = "Self::set_foo", set = "Self::set_foo_again")]
53
+ fn err_prop_already_set < T : Debug > ( span : Span , prop : & str , old : & T ) -> syn:: Error {
54
+ syn:: Error :: new (
55
+ span,
56
+ format ! (
57
+ "there is already a '{}' attribute with value: {:?}" ,
58
+ prop, old,
59
+ ) ,
60
+ )
61
+ }
62
+
63
+ // Error returned when the attr value is not a string literal (i.e. not `LitStr`)
64
+ fn err_attr_not_a_string_literal ( span : Span , attr : & str ) -> syn:: Error {
65
+ syn:: Error :: new ( span, format ! ( "'{}' value is not a string literal" , attr) )
66
+ }
67
+
68
+ /// Convert `Lit` to `LitStr`
69
+ fn extract_lit_str ( lit : & syn:: Lit ) -> Option < & syn:: LitStr > {
70
+ if let syn:: Lit :: Str ( lit_str) = lit {
71
+ Some ( lit_str)
72
+ } else {
73
+ None
74
+ }
75
+ }
76
+
49
77
pub fn add_pair ( & mut self , pair : & syn:: MetaNameValue ) -> Result < ( ) , syn:: Error > {
50
- let path_span = pair. lit . span ( ) ;
51
- let invalid_value_path = |_| {
52
- syn:: Error :: new (
53
- path_span,
54
- "Unexpected input, expected a double quoted string: \" path::to::something\" " ,
55
- )
78
+ // Update property with input value.
79
+ // Return error when there is already a value set
80
+ macro_rules! update_prop {
81
+ ( $prop: ident, $val: expr) => {
82
+ if let Some ( old) = self . $prop. replace( $val) {
83
+ return Err ( Self :: err_prop_already_set(
84
+ pair. span( ) ,
85
+ stringify!( $prop) ,
86
+ & old,
87
+ ) ) ;
88
+ }
89
+ } ;
90
+ }
91
+
92
+ // Convert input literal to a `syn::Path`
93
+ let parse_path = |prop_name| {
94
+ Self :: extract_lit_str ( & pair. lit )
95
+ . ok_or_else ( || Self :: err_attr_not_a_string_literal ( pair. span ( ) , prop_name) ) ?
96
+ . parse :: < syn:: Path > ( )
97
+ . map_err ( |_| {
98
+ syn:: Error :: new (
99
+ pair. lit . span ( ) ,
100
+ "Unexpected input, expected a double quoted string: \" path::to::something\" " ,
101
+ )
102
+ } )
56
103
} ;
104
+ // Convert input to `syn::Path`, and then update property
105
+ macro_rules! process_path_input {
106
+ ( $prop: ident$( , $( $mapping: path) ,* ) ?) => { {
107
+ let path = parse_path( stringify!( $prop) ) ?;
108
+ $(
109
+ $( let path = $mapping( path) ; ) *
110
+ ) ?
111
+ update_prop!( $prop, path) ;
112
+ } } ;
113
+ }
57
114
58
115
let name = pair
59
116
. path
60
117
. get_ident ( )
61
118
. expect ( "should be single identifier" )
62
119
. to_string ( ) ;
63
120
match name. as_str ( ) {
64
- "default" => {
65
- if let Some ( old) = self . default . replace ( pair. lit . clone ( ) ) {
66
- return Err ( syn:: Error :: new (
67
- pair. span ( ) ,
68
- format ! (
69
- "there is already a 'default' attribute with value: {:?}" ,
70
- old
71
- ) ,
72
- ) ) ;
73
- }
74
- }
121
+ "default" => update_prop ! ( default , pair. lit. clone( ) ) ,
75
122
"path" => {
76
- let string = if let syn:: Lit :: Str ( lit_str) = & pair. lit {
77
- lit_str. value ( )
78
- } else {
79
- return Err ( syn:: Error :: new (
80
- pair. span ( ) ,
81
- "'path' value is not a string literal" ,
82
- ) ) ;
83
- } ;
84
-
85
- if let Some ( old) = self . path . replace ( string) {
86
- return Err ( syn:: Error :: new (
87
- pair. span ( ) ,
88
- format ! ( "there is already a 'path' attribute with value: {:?}" , old) ,
89
- ) ) ;
90
- }
91
- }
92
- "hint" => {
93
- let string = if let syn:: Lit :: Str ( lit_str) = & pair. lit {
94
- lit_str. value ( )
95
- } else {
96
- return Err ( syn:: Error :: new (
97
- pair. span ( ) ,
98
- "'hint' value is not a string literal" ,
99
- ) ) ;
100
- } ;
101
-
102
- let path =
103
- syn:: parse_str :: < syn:: Path > ( string. as_str ( ) ) . map_err ( invalid_value_path) ?;
104
- if let Some ( old) = self . hint . replace ( path) {
105
- return Err ( syn:: Error :: new (
106
- pair. span ( ) ,
107
- format ! ( "there is already a 'hint' attribute with value: {:?}" , old) ,
108
- ) ) ;
109
- }
110
- }
111
- "get" => {
112
- let string = if let syn:: Lit :: Str ( lit_str) = & pair. lit {
113
- lit_str. value ( )
114
- } else {
115
- return Err ( syn:: Error :: new (
116
- pair. span ( ) ,
117
- "'get' value is not a string literal" ,
118
- ) ) ;
119
- } ;
120
-
121
- let path =
122
- syn:: parse_str :: < syn:: Path > ( string. as_str ( ) ) . map_err ( invalid_value_path) ?;
123
- let get = PropertyGet :: Owned ( path) ;
124
- if let Some ( old) = self . get . replace ( get) {
125
- return Err ( syn:: Error :: new (
126
- pair. span ( ) ,
127
- format ! ( "there is already a 'get' attribute with value: {:?}" , old) ,
128
- ) ) ;
129
- }
130
- }
131
- "get_ref" => {
132
- let string = if let syn:: Lit :: Str ( lit_str) = & pair. lit {
133
- lit_str. value ( )
134
- } else {
135
- return Err ( syn:: Error :: new (
136
- pair. span ( ) ,
137
- "'get_ref' value is not a string literal" ,
138
- ) ) ;
139
- } ;
140
-
141
- let path =
142
- syn:: parse_str :: < syn:: Path > ( string. as_str ( ) ) . map_err ( invalid_value_path) ?;
143
- let get_ref = PropertyGet :: Ref ( path) ;
144
- if let Some ( old) = self . get . replace ( get_ref) {
145
- return Err ( syn:: Error :: new (
146
- pair. span ( ) ,
147
- format ! (
148
- "there is already a 'get_ref' attribute with value: {:?}" ,
149
- old
150
- ) ,
151
- ) ) ;
152
- }
153
- }
154
- "set" => {
155
- let string = if let syn:: Lit :: Str ( lit_str) = & pair. lit {
156
- lit_str. value ( )
157
- } else {
158
- return Err ( syn:: Error :: new (
159
- pair. span ( ) ,
160
- "'set' value is not a string literal" ,
161
- ) ) ;
162
- } ;
163
-
164
- let path =
165
- syn:: parse_str :: < syn:: Path > ( string. as_str ( ) ) . map_err ( invalid_value_path) ?;
166
- let set = PropertySet :: WithPath ( path) ;
167
- if let Some ( old) = self . set . replace ( set) {
168
- return Err ( syn:: Error :: new (
169
- pair. span ( ) ,
170
- format ! ( "there is already a 'set' attribute with value: {:?}" , old) ,
171
- ) ) ;
172
- }
123
+ let path = Self :: extract_lit_str ( & pair. lit )
124
+ . ok_or_else ( || Self :: err_attr_not_a_string_literal ( pair. span ( ) , "path" ) ) ?;
125
+ update_prop ! ( path, path. value( ) ) ;
173
126
}
127
+ "hint" => process_path_input ! ( hint) ,
128
+ "get" => process_path_input ! ( get, PropertyGet :: Owned ) ,
129
+ "get_ref" => process_path_input ! ( get, PropertyGet :: Ref ) ,
130
+ "set" => process_path_input ! ( set, PropertySet :: WithPath ) ,
174
131
_ => {
175
132
return Err ( syn:: Error :: new (
176
133
pair. span ( ) ,
@@ -187,17 +144,11 @@ impl PropertyAttrArgsBuilder {
187
144
self . no_editor = true ;
188
145
} else if path. is_ident ( "get" ) {
189
146
if let Some ( get) = self . get . replace ( PropertyGet :: Default ) {
190
- return Err ( syn:: Error :: new (
191
- path. span ( ) ,
192
- format ! ( "there is already a 'get' attribute with value: {:?}" , get) ,
193
- ) ) ;
147
+ return Err ( Self :: err_prop_already_set ( path. span ( ) , "get" , & get) ) ;
194
148
}
195
149
} else if path. is_ident ( "set" ) {
196
150
if let Some ( set) = self . set . replace ( PropertySet :: Default ) {
197
- return Err ( syn:: Error :: new (
198
- path. span ( ) ,
199
- format ! ( "there is already a 'set' attribute with value: {:?}" , set) ,
200
- ) ) ;
151
+ return Err ( Self :: err_prop_already_set ( path. span ( ) , "set" , & set) ) ;
201
152
}
202
153
} else {
203
154
return Err ( syn:: Error :: new (
0 commit comments