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 ;
44use quote:: quote;
5- use syn:: parse:: ParseStream ;
6- use syn:: { Attribute , Expr , Fields , ItemStruct , LitStr , Meta , Token } ;
5+ use syn:: { Attribute , Expr , Fields , ItemStruct } ;
76
87use crate :: helpers:: get_docs;
8+ use crate :: parsing:: PhpRename ;
99use crate :: prelude:: * ;
1010
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 {
1414 /// The name of the PHP class. Defaults to the same name as the struct.
15- name : Option < String > ,
16- /// A modifier function which should accept one argument, a `ClassBuilder`,
17- /// and return the same object. Allows the user to modify the class before
18- /// it is built.
15+ rename : PhpRename ,
1916 modifier : Option < syn:: Ident > ,
2017 /// An expression of `ClassFlags` to be applied to the class.
2118 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 {
2819 extends : Option < syn:: Expr > ,
20+ #[ darling( multiple) ]
2921 implements : Vec < syn:: Expr > ,
30- docs : Vec < String > ,
22+ attrs : Vec < Attribute > ,
3123}
3224
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 > {
25+ pub fn parser ( mut input : ItemStruct ) -> Result < TokenStream > {
26+ let attr = StructAttributes :: from_attributes ( & input. attrs ) ?;
6527 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 ) ?;
28+ let name = attr. rename . rename ( ident. to_string ( ) ) ;
29+ let docs = get_docs ( & attr. attrs ) ?;
30+ input. attrs . retain ( |attr| !attr. path ( ) . is_ident ( "php" ) ) ;
7431
7532 let fields = match & mut input. fields {
7633 Fields :: Named ( fields) => parse_fields ( fields. named . iter_mut ( ) ) ?,
@@ -79,13 +36,13 @@ pub fn parser(args: TokenStream, mut input: ItemStruct) -> Result<TokenStream> {
7936
8037 let class_impl = generate_registered_class_impl (
8138 ident,
82- args . name . as_deref ( ) ,
83- args . modifier . as_ref ( ) ,
84- class_attrs . extends . as_ref ( ) ,
85- & class_attrs . implements ,
39+ & name,
40+ attr . modifier . as_ref ( ) ,
41+ attr . extends . as_ref ( ) ,
42+ & attr . implements ,
8643 & fields,
87- args . flags . as_ref ( ) ,
88- & class_attrs . docs ,
44+ attr . flags . as_ref ( ) ,
45+ & docs,
8946 ) ;
9047
9148 Ok ( quote ! {
@@ -96,6 +53,15 @@ pub fn parser(args: TokenStream, mut input: ItemStruct) -> Result<TokenStream> {
9653 } )
9754}
9855
56+ #[ derive( FromAttributes , Debug , Default ) ]
57+ #[ darling( attributes( php) , forward_attrs( doc) , default ) ]
58+ struct PropAttributes {
59+ prop : Flag ,
60+ rename : PhpRename ,
61+ flags : Option < Expr > ,
62+ attrs : Vec < Attribute > ,
63+ }
64+
9965fn parse_fields < ' a > ( fields : impl Iterator < Item = & ' a mut syn:: Field > ) -> Result < Vec < Property < ' a > > > {
10066 #[ derive( Debug , Default , FromMeta ) ]
10167 #[ darling( default ) ]
@@ -105,146 +71,47 @@ fn parse_fields<'a>(fields: impl Iterator<Item = &'a mut syn::Field>) -> Result<
10571
10672 let mut result = vec ! [ ] ;
10773 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- } ) ;
74+ let attr = PropAttributes :: from_attributes ( & field. attrs ) ?;
75+ if attr. prop . is_present ( ) {
76+ let ident = field
77+ . ident
78+ . as_ref ( )
79+ . ok_or_else ( || err ! ( "Only named fields can be properties." ) ) ?;
80+ let docs = get_docs ( & attr. attrs ) ?;
81+ field. attrs . retain ( |attr| !attr. path ( ) . is_ident ( "php" ) ) ;
82+
83+ result. push ( Property { ident, attr, docs } ) ;
13784 }
13885 }
13986
14087 Ok ( result)
14188}
14289
14390#[ derive( Debug ) ]
144- pub struct Property < ' a > {
91+ struct Property < ' a > {
14592 pub ident : & ' a syn:: Ident ,
146- pub attr : PropertyAttr ,
93+ pub attr : PropAttributes ,
14794 pub docs : Vec < String > ,
14895}
14996
15097impl Property < ' _ > {
15198 pub fn name ( & self ) -> String {
152- self . attr
153- . rename
154- . to_owned ( )
155- . unwrap_or_else ( || self . ident . to_string ( ) )
99+ self . attr . rename . rename ( self . ident . to_string ( ) )
156100 }
157101}
158102
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-
231103/// Generates an implementation of `RegisteredClass` for struct `ident`.
232104#[ allow( clippy:: too_many_arguments) ]
233105fn generate_registered_class_impl (
234106 ident : & syn:: Ident ,
235- class_name : Option < & str > ,
107+ class_name : & str ,
236108 modifier : Option < & syn:: Ident > ,
237109 extends : Option < & syn:: Expr > ,
238110 implements : & [ syn:: Expr ] ,
239111 fields : & [ Property ] ,
240112 flags : Option < & syn:: Expr > ,
241113 docs : & [ String ] ,
242114) -> 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- } ;
248115 let modifier = modifier. option_tokens ( ) ;
249116 let extends = extends. option_tokens ( ) ;
250117
@@ -255,7 +122,7 @@ fn generate_registered_class_impl(
255122 . attr
256123 . flags
257124 . as_ref ( )
258- . map ( |flags| flags . to_token_stream ( ) )
125+ . map ( ToTokens :: to_token_stream)
259126 . unwrap_or ( quote ! { :: ext_php_rs:: flags:: PropertyFlags :: Public } ) ;
260127 let docs = & prop. docs ;
261128
0 commit comments