@@ -3,22 +3,28 @@ use std::collections::{HashMap, HashSet};
33use crate :: class:: ClassEntryAttribute ;
44use crate :: constant:: PhpConstAttribute ;
55use crate :: function:: { Args , Function } ;
6- use crate :: helpers:: { get_docs, CleanPhpAttr } ;
7- use darling:: util:: Flag ;
6+ use crate :: helpers:: { CleanPhpAttr , get_docs} ;
87use darling:: FromAttributes ;
8+ use darling:: util:: Flag ;
99use proc_macro2:: TokenStream ;
10- use quote:: { format_ident , quote , ToTokens } ;
10+ use quote:: { ToTokens , format_ident , quote } ;
1111use syn:: { Expr , Ident , ItemTrait , Path , TraitItem , TraitItemConst , TraitItemFn } ;
1212
1313use crate :: impl_:: { FnBuilder , MethodModifier } ;
1414use crate :: parsing:: { PhpRename , RenameRule , Visibility } ;
1515use crate :: prelude:: * ;
1616
17+ const INTERNAL_INTERFACE_NAME_PREFIX : & str = "PhpInterface" ;
18+
1719#[ derive( FromAttributes , Debug , Default ) ]
1820#[ darling( attributes( php) , forward_attrs( doc) , default ) ]
19- pub struct StructAttributes {
21+ pub struct TraitAttributes {
2022 #[ darling( flatten) ]
2123 rename : PhpRename ,
24+ /// Rename methods to match the given rule.
25+ change_method_case : Option < RenameRule > ,
26+ /// Rename constants to match the given rule.
27+ change_constant_case : Option < RenameRule > ,
2228 #[ darling( multiple) ]
2329 extends : Vec < ClassEntryAttribute > ,
2430 attrs : Vec < syn:: Attribute > ,
@@ -43,7 +49,7 @@ struct InterfaceData<'a> {
4349 ident : & ' a Ident ,
4450 name : String ,
4551 path : Path ,
46- attrs : StructAttributes ,
52+ extends : Vec < ClassEntryAttribute > ,
4753 constructor : Option < Function < ' a > > ,
4854 methods : Vec < FnBuilder > ,
4955 constants : Vec < Constant < ' a > > ,
@@ -53,9 +59,9 @@ struct InterfaceData<'a> {
5359impl ToTokens for InterfaceData < ' _ > {
5460 #[ allow( clippy:: too_many_lines) ]
5561 fn to_tokens ( & self , tokens : & mut TokenStream ) {
56- let interface_name = format_ident ! ( "PhpInterface {}" , self . ident) ;
62+ let interface_name = format_ident ! ( "{INTERNAL_INTERFACE_NAME_PREFIX} {}" , self . ident) ;
5763 let name = & self . name ;
58- let implements = & self . attrs . extends ;
64+ let implements = & self . extends ;
5965 let methods_sig = & self . methods ;
6066 let constants = & self . constants ;
6167 let docs = & self . docs ;
@@ -188,19 +194,19 @@ impl ToTokens for InterfaceData<'_> {
188194
189195impl < ' a > Parse < ' a , InterfaceData < ' a > > for ItemTrait {
190196 fn parse ( & ' a mut self ) -> Result < InterfaceData < ' a > > {
191- let attrs = StructAttributes :: from_attributes ( & self . attrs ) ?;
197+ let attrs = TraitAttributes :: from_attributes ( & self . attrs ) ?;
192198 let ident = & self . ident ;
193199 let name = attrs. rename . rename ( ident. to_string ( ) , RenameRule :: Pascal ) ;
194200 let docs = get_docs ( & attrs. attrs ) ?;
195201 self . attrs . clean_php ( ) ;
196- let interface_name = format_ident ! ( "PhpInterface {ident}" ) ;
202+ let interface_name = format_ident ! ( "{INTERNAL_INTERFACE_NAME_PREFIX} {ident}" ) ;
197203 let ts = quote ! { #interface_name } ;
198204 let path: Path = syn:: parse2 ( ts) ?;
199205 let mut data = InterfaceData {
200206 ident,
201207 name,
202208 path,
203- attrs,
209+ extends : attrs. extends ,
204210 constructor : None ,
205211 methods : Vec :: default ( ) ,
206212 constants : Vec :: default ( ) ,
@@ -209,17 +215,17 @@ impl<'a> Parse<'a, InterfaceData<'a>> for ItemTrait {
209215
210216 for item in & mut self . items {
211217 match item {
212- TraitItem :: Fn ( f) => {
213- match f. parse ( ) ? {
214- MethodKind :: Method ( builder) => data. methods . push ( builder) ,
215- MethodKind :: Constructor ( builder) => {
216- if data. constructor . replace ( builder) . is_some ( ) {
217- bail ! ( "Only one constructor can be provided per class." ) ;
218- }
218+ TraitItem :: Fn ( f) => match parse_trait_item_fn ( f, attrs. change_method_case ) ? {
219+ MethodKind :: Method ( builder) => data. methods . push ( builder) ,
220+ MethodKind :: Constructor ( builder) => {
221+ if data. constructor . replace ( builder) . is_some ( ) {
222+ bail ! ( "Only one constructor can be provided per class." ) ;
219223 }
220- } ;
221- }
222- TraitItem :: Const ( c) => data. constants . push ( c. parse ( ) ?) ,
224+ }
225+ } ,
226+ TraitItem :: Const ( c) => data
227+ . constants
228+ . push ( parse_trait_item_const ( c, attrs. change_constant_case ) ?) ,
223229 _ => { }
224230 }
225231 }
@@ -247,63 +253,51 @@ enum MethodKind<'a> {
247253 Constructor ( Function < ' a > ) ,
248254}
249255
250- impl < ' a > Parse < ' a , MethodKind < ' a > > for TraitItemFn {
251- fn parse ( & ' a mut self ) -> Result < MethodKind < ' a > > {
252- if self . default . is_some ( ) {
253- bail ! ( self => "Interface could not have default impl" ) ;
254- }
256+ fn parse_trait_item_fn (
257+ fn_item : & mut TraitItemFn ,
258+ change_case : Option < RenameRule > ,
259+ ) -> Result < MethodKind < ' _ > > {
260+ if fn_item. default . is_some ( ) {
261+ bail ! ( fn_item => "Interface an not have default impl" ) ;
262+ }
255263
256- let php_attr = PhpFunctionInterfaceAttribute :: from_attributes ( & self . attrs ) ?;
257- self . attrs . clean_php ( ) ;
264+ let php_attr = PhpFunctionInterfaceAttribute :: from_attributes ( & fn_item . attrs ) ?;
265+ fn_item . attrs . clean_php ( ) ;
258266
259- let mut args = Args :: parse_from_fnargs ( self . sig . inputs . iter ( ) , php_attr. defaults ) ?;
267+ let mut args = Args :: parse_from_fnargs ( fn_item . sig . inputs . iter ( ) , php_attr. defaults ) ?;
260268
261- let docs = get_docs ( & php_attr. attrs ) ?;
269+ let docs = get_docs ( & php_attr. attrs ) ?;
262270
263- let mut modifiers: HashSet < MethodModifier > = HashSet :: new ( ) ;
264- modifiers. insert ( MethodModifier :: Abstract ) ;
271+ let mut modifiers: HashSet < MethodModifier > = HashSet :: new ( ) ;
272+ modifiers. insert ( MethodModifier :: Abstract ) ;
265273
266- if args. typed . first ( ) . is_some_and ( |arg| arg. name == "self_" ) {
267- args. typed . pop ( ) ;
268- } else if args. receiver . is_none ( ) {
269- modifiers. insert ( MethodModifier :: Static ) ;
270- }
271-
272- let f = Function :: new (
273- & self . sig ,
274- php_attr
275- . rename
276- . rename ( self . sig . ident . to_string ( ) , RenameRule :: Camel ) ,
277- args,
278- php_attr. optional ,
279- docs,
280- ) ;
281-
282- if php_attr. constructor . is_present ( ) {
283- Ok ( MethodKind :: Constructor ( f) )
284- } else {
285- let builder = FnBuilder {
286- builder : f. abstract_function_builder ( ) ,
287- vis : php_attr. vis . unwrap_or ( Visibility :: Public ) ,
288- modifiers,
289- } ;
290-
291- Ok ( MethodKind :: Method ( builder) )
292- }
274+ if args. typed . first ( ) . is_some_and ( |arg| arg. name == "self_" ) {
275+ args. typed . pop ( ) ;
276+ } else if args. receiver . is_none ( ) {
277+ modifiers. insert ( MethodModifier :: Static ) ;
293278 }
294- }
295279
296- impl < ' a > Parse < ' a , Vec < MethodKind < ' a > > > for ItemTrait {
297- fn parse ( & ' a mut self ) -> Result < Vec < MethodKind < ' a > > > {
298- Ok ( self
299- . items
300- . iter_mut ( )
301- . filter_map ( |item| match item {
302- TraitItem :: Fn ( f) => Some ( f) ,
303- _ => None ,
304- } )
305- . flat_map ( Parse :: parse)
306- . collect ( ) )
280+ let f = Function :: new (
281+ & fn_item. sig ,
282+ php_attr. rename . rename (
283+ fn_item. sig . ident . to_string ( ) ,
284+ change_case. unwrap_or ( RenameRule :: Camel ) ,
285+ ) ,
286+ args,
287+ php_attr. optional ,
288+ docs,
289+ ) ;
290+
291+ if php_attr. constructor . is_present ( ) {
292+ Ok ( MethodKind :: Constructor ( f) )
293+ } else {
294+ let builder = FnBuilder {
295+ builder : f. abstract_function_builder ( ) ,
296+ vis : php_attr. vis . unwrap_or ( Visibility :: Public ) ,
297+ modifiers,
298+ } ;
299+
300+ Ok ( MethodKind :: Method ( builder) )
307301 }
308302}
309303
@@ -332,32 +326,22 @@ impl<'a> Constant<'a> {
332326 }
333327}
334328
335- impl < ' a > Parse < ' a , Constant < ' a > > for TraitItemConst {
336- fn parse ( & ' a mut self ) -> Result < Constant < ' a > > {
337- if self . default . is_none ( ) {
338- bail ! ( self => "Interface const could not be empty" ) ;
339- }
340-
341- let attr = PhpConstAttribute :: from_attributes ( & self . attrs ) ?;
342- let name = self . ident . to_string ( ) ;
343- let docs = get_docs ( & attr. attrs ) ?;
344- self . attrs . clean_php ( ) ;
345-
346- let ( _, expr) = self . default . as_ref ( ) . unwrap ( ) ;
347- Ok ( Constant :: new ( name, expr, docs) )
329+ fn parse_trait_item_const (
330+ const_item : & mut TraitItemConst ,
331+ change_case : Option < RenameRule > ,
332+ ) -> Result < Constant < ' _ > > {
333+ if const_item. default . is_none ( ) {
334+ bail ! ( const_item => "PHP Interface const can not be empty" ) ;
348335 }
349- }
350336
351- impl < ' a > Parse < ' a , Vec < Constant < ' a > > > for ItemTrait {
352- fn parse ( & ' a mut self ) -> Result < Vec < Constant < ' a > > > {
353- Ok ( self
354- . items
355- . iter_mut ( )
356- . filter_map ( |item| match item {
357- TraitItem :: Const ( c) => Some ( c) ,
358- _ => None ,
359- } )
360- . flat_map ( Parse :: parse)
361- . collect ( ) )
362- }
337+ let attr = PhpConstAttribute :: from_attributes ( & const_item. attrs ) ?;
338+ let name = attr. rename . rename (
339+ const_item. ident . to_string ( ) ,
340+ change_case. unwrap_or ( RenameRule :: ScreamingSnake ) ,
341+ ) ;
342+ let docs = get_docs ( & attr. attrs ) ?;
343+ const_item. attrs . clean_php ( ) ;
344+
345+ let ( _, expr) = const_item. default . as_ref ( ) . unwrap ( ) ;
346+ Ok ( Constant :: new ( name, expr, docs) )
363347}
0 commit comments