1+ use std:: collections:: { HashMap , HashSet } ;
2+
3+ use darling:: util:: Flag ;
14use darling:: { FromAttributes } ;
25use proc_macro2:: TokenStream ;
36use quote:: { format_ident, quote} ;
4- use syn:: { ItemTrait , TraitItem , TraitItemFn } ;
5- use crate :: helpers:: CleanPhpAttr ;
7+ use syn:: { Expr , Ident , ItemTrait , Path , TraitItem , TraitItemConst , TraitItemFn } ;
8+ use crate :: class:: ClassEntryAttribute ;
9+ use crate :: constant:: PhpConstAttribute ;
10+ use crate :: function:: { Args , Function } ;
11+ use crate :: helpers:: { get_docs, CleanPhpAttr } ;
612
7- use crate :: parsing:: { PhpRename , RenameRule } ;
13+ use crate :: impl_:: { Constant , FnBuilder , MethodModifier } ;
14+ use crate :: parsing:: { PhpRename , RenameRule , Visibility } ;
815use crate :: prelude:: * ;
916
1017#[ derive( FromAttributes , Debug , Default ) ]
1118#[ darling( attributes( php) , forward_attrs( doc) , default ) ]
1219pub struct StructAttributes {
1320 #[ darling( flatten) ]
1421 rename : PhpRename ,
22+ #[ darling( multiple) ]
23+ extends : Vec < ClassEntryAttribute > ,
1524}
1625
1726pub fn parser ( mut input : ItemTrait ) -> Result < TokenStream > {
1827 let attr = StructAttributes :: from_attributes ( & input. attrs ) ?;
1928 let ident = & input. ident ;
2029
2130 let interface_name = format_ident ! ( "PhpInterface{ident}" ) ;
31+ let ts = quote ! { #interface_name } ;
32+ let path: Path = syn:: parse2 ( ts) ?;
33+
2234 let name = attr. rename . rename ( ident. to_string ( ) , RenameRule :: Pascal ) ;
2335 input. attrs . clean_php ( ) ;
2436
25- let mut interface_methods : Vec < TraitItemFn > = Vec :: new ( ) ;
26- for i in input . items . clone ( ) . into_iter ( ) {
27- match i {
28- TraitItem :: Fn ( f ) => {
29- if f . default . is_some ( ) {
30- bail ! ( "Interface could not have default impl" ) ;
37+ let methods : Vec < FnBuilder > = input . items . iter_mut ( )
38+ . flat_map (
39+ | item : & mut TraitItem | {
40+ match item {
41+ TraitItem :: Fn ( f ) => Some ( f ) ,
42+ _ => None ,
3143 }
32- interface_methods. push ( f) ;
44+ } )
45+ . flat_map ( |f| f. parse ( ) )
46+ . collect ( ) ;
47+
48+ let constants: Vec < _ > = input. items . iter_mut ( )
49+ . flat_map ( |item : & mut TraitItem | {
50+ match item {
51+ TraitItem :: Const ( c) => Some ( c) ,
52+ _ => None ,
3353 }
34- _ => { }
54+ } )
55+ . flat_map ( |c| c. parse ( ) )
56+ . map ( |c| {
57+ let name = & c. name ;
58+ let ident = c. ident ;
59+ let docs = & c. docs ;
60+ quote ! {
61+ ( #name, & #path:: #ident, & [ #( #docs) , * ] )
62+ }
63+ } )
64+ . collect ( ) ;
65+
66+ let impl_const: Vec < & TraitItemConst > = input. items . iter ( ) . flat_map ( |item| {
67+ match item {
68+ TraitItem :: Const ( c) => Some ( c) ,
69+ _ => None ,
3570 }
36- } ;
71+ } )
72+ . map ( |c| {
73+ if c. default . is_none ( ) {
74+ bail ! ( "Interface const canot be empty" ) ;
75+ }
76+ Ok ( c)
77+ } )
78+ . flat_map ( |c| c)
79+ . collect ( ) ;
80+
81+ let implements = attr. extends ;
3782
3883 Ok ( quote ! {
3984 #input
4085
4186 pub struct #interface_name;
4287
88+ impl #interface_name {
89+ #( pub #impl_const) *
90+ }
91+
4392 impl :: ext_php_rs:: class:: RegisteredClass for #interface_name {
4493 const CLASS_NAME : & ' static str = #name;
4594
@@ -51,7 +100,9 @@ pub fn parser(mut input: ItemTrait) -> Result<TokenStream> {
51100
52101 const FLAGS : :: ext_php_rs:: flags:: ClassFlags = :: ext_php_rs:: flags:: ClassFlags :: Interface ;
53102
54- const IMPLEMENTS : & ' static [ :: ext_php_rs:: class:: ClassEntryInfo ] = & [ ] ;
103+ const IMPLEMENTS : & ' static [ :: ext_php_rs:: class:: ClassEntryInfo ] = & [
104+ #( #implements, ) *
105+ ] ;
55106
56107 fn get_metadata( ) -> & ' static :: ext_php_rs:: class:: ClassMetadata <Self > {
57108 static METADATA : :: ext_php_rs:: class:: ClassMetadata <#interface_name> =
@@ -64,7 +115,7 @@ pub fn parser(mut input: ItemTrait) -> Result<TokenStream> {
64115 :: ext_php_rs:: builders:: FunctionBuilder <' static >,
65116 :: ext_php_rs:: flags:: MethodFlags ,
66117 ) > {
67- vec![ ]
118+ vec![ # ( #methods ) , * ]
68119 }
69120
70121 fn constructor( ) -> Option <:: ext_php_rs:: class:: ConstructorMeta <Self >> {
@@ -76,14 +127,37 @@ pub fn parser(mut input: ItemTrait) -> Result<TokenStream> {
76127 & ' static dyn ext_php_rs:: convert:: IntoZvalDyn ,
77128 ext_php_rs:: describe:: DocComments ,
78129 ) ] {
79- & [ ]
130+ use :: ext_php_rs:: internal:: class:: PhpClassImpl ;
131+ :: ext_php_rs:: internal:: class:: PhpClassImplCollector :: <Self >:: default ( ) . get_constants( )
80132 }
81133
82134 fn get_properties<' a>( ) -> std:: collections:: HashMap <& ' static str , :: ext_php_rs:: internal:: property:: PropertyInfo <' a, Self >> {
83135 HashMap :: new( )
84136 }
85137
86138 }
139+ impl :: ext_php_rs:: internal:: class:: PhpClassImpl <#path>
140+ for :: ext_php_rs:: internal:: class:: PhpClassImplCollector <#path>
141+ {
142+ fn get_methods( self ) -> :: std:: vec:: Vec <
143+ ( :: ext_php_rs:: builders:: FunctionBuilder <' static >, :: ext_php_rs:: flags:: MethodFlags )
144+ > {
145+ vec![ ]
146+ }
147+
148+ fn get_method_props<' a>( self ) -> :: std:: collections:: HashMap <& ' static str , :: ext_php_rs:: props:: Property <' a, #path>> {
149+ todo!( )
150+ }
151+
152+ fn get_constructor( self ) -> :: std:: option:: Option <:: ext_php_rs:: class:: ConstructorMeta <#path>> {
153+ None
154+ }
155+
156+ fn get_constants( self ) -> & ' static [ ( & ' static str , & ' static dyn :: ext_php_rs:: convert:: IntoZvalDyn , & ' static [ & ' static str ] ) ] {
157+ & [ #( #constants) , * ]
158+ }
159+ }
160+
87161
88162 impl <' a> :: ext_php_rs:: convert:: FromZendObject <' a> for & ' a #interface_name {
89163 #[ inline]
@@ -150,3 +224,75 @@ pub fn parser(mut input: ItemTrait) -> Result<TokenStream> {
150224 }
151225 } )
152226}
227+
228+ #[ derive( FromAttributes , Default , Debug ) ]
229+ #[ darling( default , attributes( php) , forward_attrs( doc) ) ]
230+ pub struct PhpFunctionInterfaceAttribute {
231+ #[ darling( flatten) ]
232+ rename : PhpRename ,
233+ defaults : HashMap < Ident , Expr > ,
234+ optional : Option < Ident > ,
235+ vis : Option < Visibility > ,
236+ attrs : Vec < syn:: Attribute > ,
237+ getter : Flag ,
238+ setter : Flag ,
239+ constructor : Flag ,
240+ }
241+
242+ trait Parse < ' a , T > {
243+ fn parse ( & ' a mut self ) -> Result < T > ;
244+ }
245+
246+ impl < ' a > Parse < ' a , Constant < ' a > > for TraitItemConst {
247+ fn parse ( & ' a mut self ) -> Result < Constant < ' a > > {
248+ let attr = PhpConstAttribute :: from_attributes ( & self . attrs ) ?;
249+ let name = self . ident . to_string ( ) ;
250+ let docs = get_docs ( & attr. attrs ) ?;
251+ self . attrs . clean_php ( ) ;
252+
253+ Ok ( Constant :: new ( name, & self . ident , docs) )
254+ }
255+ }
256+
257+ impl < ' a > Parse < ' a , FnBuilder > for TraitItemFn {
258+ fn parse ( & ' a mut self ) -> Result < FnBuilder > {
259+ let php_attr = PhpFunctionInterfaceAttribute :: from_attributes (
260+ & self . attrs
261+ ) ?;
262+ if self . default . is_some ( ) {
263+ bail ! ( "Interface could not have default impl" ) ;
264+ }
265+
266+ let mut args = Args :: parse_from_fnargs (
267+ self . sig . inputs . iter ( ) ,
268+ php_attr. defaults
269+ ) ?;
270+ let docs = get_docs ( & php_attr. attrs ) ?;
271+
272+ self . attrs . clean_php ( ) ;
273+
274+ let mut modifiers: HashSet < MethodModifier > = HashSet :: new ( ) ;
275+ modifiers. insert ( MethodModifier :: Abstract ) ;
276+ if args. typed . first ( ) . is_some_and ( |arg| arg. name == "self_" ) {
277+ args. typed . pop ( ) ;
278+ } else if args. receiver . is_none ( ) {
279+ modifiers. insert ( MethodModifier :: Static ) ;
280+ } ;
281+
282+ let f = Function :: new (
283+ & self . sig ,
284+ php_attr
285+ . rename
286+ . rename ( self . sig . ident . to_string ( ) , RenameRule :: Camel ) ,
287+ args,
288+ php_attr. optional ,
289+ docs,
290+ ) ;
291+
292+ Ok ( FnBuilder {
293+ builder : f. abstract_function_builder ( ) ,
294+ vis : php_attr. vis . unwrap_or ( Visibility :: Public ) ,
295+ modifiers,
296+ } )
297+ }
298+ }
0 commit comments