1
- use darling:: ast :: NestedMeta ;
2
- use darling:: { FromMeta , ToTokens } ;
3
- use proc_macro2:: { Ident , TokenStream } ;
1
+ use darling:: util :: Flag ;
2
+ use darling:: { FromAttributes , FromMeta , ToTokens } ;
3
+ use proc_macro2:: TokenStream ;
4
4
use quote:: quote;
5
- use syn:: parse:: ParseStream ;
6
- use syn:: { Attribute , Expr , Fields , ItemStruct , LitStr , Meta , Token } ;
5
+ use syn:: { Attribute , Expr , Fields , ItemStruct } ;
7
6
8
7
use crate :: helpers:: get_docs;
8
+ use crate :: parsing:: PhpRename ;
9
9
use crate :: prelude:: * ;
10
10
11
- #[ derive( Debug , Default , FromMeta ) ]
12
- #[ darling( default ) ]
13
- pub struct StructArgs {
11
+ #[ derive( FromAttributes , Debug , Default ) ]
12
+ #[ darling( attributes ( php ) , forward_attrs ( doc ) , default ) ]
13
+ pub struct StructAttributes {
14
14
/// The name of the PHP class. Defaults to the same name as the struct.
15
- name : Option < String > ,
15
+ #[ darling( flatten) ]
16
+ rename : PhpRename ,
16
17
/// A modifier function which should accept one argument, a `ClassBuilder`,
17
18
/// and return the same object. Allows the user to modify the class before
18
19
/// it is built.
19
20
modifier : Option < syn:: Ident > ,
20
21
/// An expression of `ClassFlags` to be applied to the class.
21
22
flags : Option < syn:: Expr > ,
22
- }
23
-
24
- /// Sub-attributes which are parsed by this macro. Must be placed underneath the
25
- /// main `#[php_class]` attribute.
26
- #[ derive( Debug , Default ) ]
27
- struct ClassAttrs {
28
23
extends : Option < syn:: Expr > ,
24
+ #[ darling( multiple) ]
29
25
implements : Vec < syn:: Expr > ,
30
- docs : Vec < String > ,
26
+ attrs : Vec < Attribute > ,
31
27
}
32
28
33
- impl ClassAttrs {
34
- fn parse ( & mut self , attrs : & mut Vec < syn:: Attribute > ) -> Result < ( ) > {
35
- let mut unparsed = vec ! [ ] ;
36
- unparsed. append ( attrs) ;
37
- for attr in unparsed {
38
- let path = attr. path ( ) ;
39
-
40
- if path. is_ident ( "extends" ) {
41
- if self . extends . is_some ( ) {
42
- bail ! ( attr => "Only one `#[extends]` attribute is valid per struct." ) ;
43
- }
44
- let extends: syn:: Expr = match attr. parse_args ( ) {
45
- Ok ( extends) => extends,
46
- Err ( _) => bail ! ( attr => "Invalid arguments passed to extends attribute." ) ,
47
- } ;
48
- self . extends = Some ( extends) ;
49
- } else if path. is_ident ( "implements" ) {
50
- let implements: syn:: Expr = match attr. parse_args ( ) {
51
- Ok ( extends) => extends,
52
- Err ( _) => bail ! ( attr => "Invalid arguments passed to implements attribute." ) ,
53
- } ;
54
- self . implements . push ( implements) ;
55
- } else {
56
- attrs. push ( attr) ;
57
- }
58
- }
59
- self . docs = get_docs ( attrs) ;
60
- Ok ( ( ) )
61
- }
62
- }
63
-
64
- pub fn parser ( args : TokenStream , mut input : ItemStruct ) -> Result < TokenStream > {
29
+ pub fn parser ( mut input : ItemStruct ) -> Result < TokenStream > {
30
+ let attr = StructAttributes :: from_attributes ( & input. attrs ) ?;
65
31
let ident = & input. ident ;
66
- let meta = NestedMeta :: parse_meta_list ( args) ?;
67
- let args = match StructArgs :: from_list ( & meta) {
68
- Ok ( args) => args,
69
- Err ( e) => bail ! ( "Failed to parse struct arguments: {:?}" , e) ,
70
- } ;
71
-
72
- let mut class_attrs = ClassAttrs :: default ( ) ;
73
- class_attrs. parse ( & mut input. attrs ) ?;
32
+ let name = attr. rename . rename ( ident. to_string ( ) ) ;
33
+ let docs = get_docs ( & attr. attrs ) ?;
34
+ input. attrs . retain ( |attr| !attr. path ( ) . is_ident ( "php" ) ) ;
74
35
75
36
let fields = match & mut input. fields {
76
37
Fields :: Named ( fields) => parse_fields ( fields. named . iter_mut ( ) ) ?,
@@ -79,13 +40,13 @@ pub fn parser(args: TokenStream, mut input: ItemStruct) -> Result<TokenStream> {
79
40
80
41
let class_impl = generate_registered_class_impl (
81
42
ident,
82
- args . name . as_deref ( ) ,
83
- args . modifier . as_ref ( ) ,
84
- class_attrs . extends . as_ref ( ) ,
85
- & class_attrs . implements ,
43
+ & name,
44
+ attr . modifier . as_ref ( ) ,
45
+ attr . extends . as_ref ( ) ,
46
+ & attr . implements ,
86
47
& fields,
87
- args . flags . as_ref ( ) ,
88
- & class_attrs . docs ,
48
+ attr . flags . as_ref ( ) ,
49
+ & docs,
89
50
) ;
90
51
91
52
Ok ( quote ! {
@@ -96,6 +57,16 @@ pub fn parser(args: TokenStream, mut input: ItemStruct) -> Result<TokenStream> {
96
57
} )
97
58
}
98
59
60
+ #[ derive( FromAttributes , Debug , Default ) ]
61
+ #[ darling( attributes( php) , forward_attrs( doc) , default ) ]
62
+ struct PropAttributes {
63
+ prop : Flag ,
64
+ #[ darling( flatten) ]
65
+ rename : PhpRename ,
66
+ flags : Option < Expr > ,
67
+ attrs : Vec < Attribute > ,
68
+ }
69
+
99
70
fn parse_fields < ' a > ( fields : impl Iterator < Item = & ' a mut syn:: Field > ) -> Result < Vec < Property < ' a > > > {
100
71
#[ derive( Debug , Default , FromMeta ) ]
101
72
#[ darling( default ) ]
@@ -105,146 +76,47 @@ fn parse_fields<'a>(fields: impl Iterator<Item = &'a mut syn::Field>) -> Result<
105
76
106
77
let mut result = vec ! [ ] ;
107
78
for field in fields {
108
- let mut docs = vec ! [ ] ;
109
- let mut property = None ;
110
- let mut unparsed = vec ! [ ] ;
111
- unparsed. append ( & mut field. attrs ) ;
112
-
113
- for attr in unparsed {
114
- if let Some ( parsed) = parse_attribute ( & attr) ? {
115
- match parsed {
116
- ParsedAttribute :: Property ( prop) => {
117
- let ident = field
118
- . ident
119
- . as_ref ( )
120
- . ok_or_else ( || err ! ( attr => "Only named fields can be properties." ) ) ?;
121
-
122
- property = Some ( ( ident, prop) ) ;
123
- }
124
- ParsedAttribute :: Comment ( doc) => docs. push ( doc) ,
125
- }
126
- } else {
127
- field. attrs . push ( attr) ;
128
- }
129
- }
130
-
131
- if let Some ( ( ident, prop) ) = property {
132
- result. push ( Property {
133
- ident,
134
- attr : prop,
135
- docs,
136
- } ) ;
79
+ let attr = PropAttributes :: from_attributes ( & field. attrs ) ?;
80
+ if attr. prop . is_present ( ) {
81
+ let ident = field
82
+ . ident
83
+ . as_ref ( )
84
+ . ok_or_else ( || err ! ( "Only named fields can be properties." ) ) ?;
85
+ let docs = get_docs ( & attr. attrs ) ?;
86
+ field. attrs . retain ( |attr| !attr. path ( ) . is_ident ( "php" ) ) ;
87
+
88
+ result. push ( Property { ident, attr, docs } ) ;
137
89
}
138
90
}
139
91
140
92
Ok ( result)
141
93
}
142
94
143
95
#[ derive( Debug ) ]
144
- pub struct Property < ' a > {
96
+ struct Property < ' a > {
145
97
pub ident : & ' a syn:: Ident ,
146
- pub attr : PropertyAttr ,
98
+ pub attr : PropAttributes ,
147
99
pub docs : Vec < String > ,
148
100
}
149
101
150
102
impl Property < ' _ > {
151
103
pub fn name ( & self ) -> String {
152
- self . attr
153
- . rename
154
- . to_owned ( )
155
- . unwrap_or_else ( || self . ident . to_string ( ) )
104
+ self . attr . rename . rename ( self . ident . to_string ( ) )
156
105
}
157
106
}
158
107
159
- #[ derive( Debug , Default ) ]
160
- pub struct PropertyAttr {
161
- pub rename : Option < String > ,
162
- pub flags : Option < Expr > ,
163
- }
164
-
165
- impl syn:: parse:: Parse for PropertyAttr {
166
- fn parse ( input : syn:: parse:: ParseStream ) -> syn:: Result < Self > {
167
- let mut this = Self :: default ( ) ;
168
- while !input. is_empty ( ) {
169
- let field = input. parse :: < Ident > ( ) ?. to_string ( ) ;
170
- input. parse :: < Token ! [ =] > ( ) ?;
171
-
172
- match field. as_str ( ) {
173
- "rename" => {
174
- this. rename . replace ( input. parse :: < LitStr > ( ) ?. value ( ) ) ;
175
- }
176
- "flags" => {
177
- this. flags . replace ( input. parse :: < Expr > ( ) ?) ;
178
- }
179
- _ => return Err ( input. error ( "invalid attribute field" ) ) ,
180
- }
181
-
182
- let _ = input. parse :: < Token ! [ , ] > ( ) ;
183
- }
184
-
185
- Ok ( this)
186
- }
187
- }
188
-
189
- #[ derive( Debug ) ]
190
- pub enum ParsedAttribute {
191
- Property ( PropertyAttr ) ,
192
- Comment ( String ) ,
193
- }
194
-
195
- pub fn parse_attribute ( attr : & Attribute ) -> Result < Option < ParsedAttribute > > {
196
- let name = attr. path ( ) . to_token_stream ( ) . to_string ( ) ;
197
-
198
- Ok ( match name. as_ref ( ) {
199
- "doc" => {
200
- struct DocComment ( pub String ) ;
201
-
202
- impl syn:: parse:: Parse for DocComment {
203
- fn parse ( input : ParseStream ) -> syn:: Result < Self > {
204
- input. parse :: < Token ! [ =] > ( ) ?;
205
- let comment: LitStr = input. parse ( ) ?;
206
- Ok ( Self ( comment. value ( ) ) )
207
- }
208
- }
209
-
210
- let comment: DocComment = syn:: parse2 ( attr. to_token_stream ( ) )
211
- . map_err ( |e| err ! ( attr => "Failed to parse doc comment {}" , e) ) ?;
212
- Some ( ParsedAttribute :: Comment ( comment. 0 ) )
213
- }
214
- "prop" | "property" => {
215
- let attr = match attr. meta {
216
- Meta :: Path ( _) => PropertyAttr :: default ( ) ,
217
- Meta :: List ( _) => attr
218
- . parse_args ( )
219
- . map_err ( |e| err ! ( attr => "Unable to parse `#[{}]` attribute: {}" , name, e) ) ?,
220
- _ => {
221
- bail ! ( attr => "Invalid attribute format for `#[{}]`" , name) ;
222
- }
223
- } ;
224
-
225
- Some ( ParsedAttribute :: Property ( attr) )
226
- }
227
- _ => None ,
228
- } )
229
- }
230
-
231
108
/// Generates an implementation of `RegisteredClass` for struct `ident`.
232
109
#[ allow( clippy:: too_many_arguments) ]
233
110
fn generate_registered_class_impl (
234
111
ident : & syn:: Ident ,
235
- class_name : Option < & str > ,
112
+ class_name : & str ,
236
113
modifier : Option < & syn:: Ident > ,
237
114
extends : Option < & syn:: Expr > ,
238
115
implements : & [ syn:: Expr ] ,
239
116
fields : & [ Property ] ,
240
117
flags : Option < & syn:: Expr > ,
241
118
docs : & [ String ] ,
242
119
) -> TokenStream {
243
- let ident_str = ident. to_string ( ) ;
244
- let class_name = match class_name {
245
- Some ( class_name) => class_name,
246
- None => & ident_str,
247
- } ;
248
120
let modifier = modifier. option_tokens ( ) ;
249
121
let extends = extends. option_tokens ( ) ;
250
122
@@ -255,7 +127,7 @@ fn generate_registered_class_impl(
255
127
. attr
256
128
. flags
257
129
. as_ref ( )
258
- . map ( |flags| flags . to_token_stream ( ) )
130
+ . map ( ToTokens :: to_token_stream)
259
131
. unwrap_or ( quote ! { :: ext_php_rs:: flags:: PropertyFlags :: Public } ) ;
260
132
let docs = & prop. docs ;
261
133
0 commit comments