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 > ,
15+ #[ darling( flatten) ]
16+ rename : PhpRename ,
1617 /// A modifier function which should accept one argument, a `ClassBuilder`,
1718 /// and return the same object. Allows the user to modify the class before
1819 /// it is built.
1920 modifier : Option < syn:: Ident > ,
2021 /// An expression of `ClassFlags` to be applied to the class.
2122 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 {
2823 extends : Option < syn:: Expr > ,
24+ #[ darling( multiple) ]
2925 implements : Vec < syn:: Expr > ,
30- docs : Vec < String > ,
26+ attrs : Vec < Attribute > ,
3127}
3228
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 ) ?;
6531 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" ) ) ;
7435
7536 let fields = match & mut input. fields {
7637 Fields :: Named ( fields) => parse_fields ( fields. named . iter_mut ( ) ) ?,
@@ -79,13 +40,13 @@ pub fn parser(args: TokenStream, mut input: ItemStruct) -> Result<TokenStream> {
7940
8041 let class_impl = generate_registered_class_impl (
8142 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 ,
8647 & fields,
87- args . flags . as_ref ( ) ,
88- & class_attrs . docs ,
48+ attr . flags . as_ref ( ) ,
49+ & docs,
8950 ) ;
9051
9152 Ok ( quote ! {
@@ -96,6 +57,16 @@ pub fn parser(args: TokenStream, mut input: ItemStruct) -> Result<TokenStream> {
9657 } )
9758}
9859
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+
9970fn parse_fields < ' a > ( fields : impl Iterator < Item = & ' a mut syn:: Field > ) -> Result < Vec < Property < ' a > > > {
10071 #[ derive( Debug , Default , FromMeta ) ]
10172 #[ darling( default ) ]
@@ -105,146 +76,47 @@ fn parse_fields<'a>(fields: impl Iterator<Item = &'a mut syn::Field>) -> Result<
10576
10677 let mut result = vec ! [ ] ;
10778 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 } ) ;
13789 }
13890 }
13991
14092 Ok ( result)
14193}
14294
14395#[ derive( Debug ) ]
144- pub struct Property < ' a > {
96+ struct Property < ' a > {
14597 pub ident : & ' a syn:: Ident ,
146- pub attr : PropertyAttr ,
98+ pub attr : PropAttributes ,
14799 pub docs : Vec < String > ,
148100}
149101
150102impl Property < ' _ > {
151103 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 ( ) )
156105 }
157106}
158107
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-
231108/// Generates an implementation of `RegisteredClass` for struct `ident`.
232109#[ allow( clippy:: too_many_arguments) ]
233110fn generate_registered_class_impl (
234111 ident : & syn:: Ident ,
235- class_name : Option < & str > ,
112+ class_name : & str ,
236113 modifier : Option < & syn:: Ident > ,
237114 extends : Option < & syn:: Expr > ,
238115 implements : & [ syn:: Expr ] ,
239116 fields : & [ Property ] ,
240117 flags : Option < & syn:: Expr > ,
241118 docs : & [ String ] ,
242119) -> 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- } ;
248120 let modifier = modifier. option_tokens ( ) ;
249121 let extends = extends. option_tokens ( ) ;
250122
@@ -255,7 +127,7 @@ fn generate_registered_class_impl(
255127 . attr
256128 . flags
257129 . as_ref ( )
258- . map ( |flags| flags . to_token_stream ( ) )
130+ . map ( ToTokens :: to_token_stream)
259131 . unwrap_or ( quote ! { :: ext_php_rs:: flags:: PropertyFlags :: Public } ) ;
260132 let docs = & prop. docs ;
261133
0 commit comments