@@ -26,6 +26,7 @@ pub struct Arg {
26
26
pub ty : String ,
27
27
pub nullable : bool ,
28
28
pub default : Option < String > ,
29
+ pub as_ref : bool ,
29
30
}
30
31
31
32
#[ derive( Debug , Clone ) ]
@@ -249,12 +250,19 @@ pub fn get_return_type(output_type: &ReturnType) -> Result<Option<(String, bool)
249
250
}
250
251
251
252
impl Arg {
252
- pub fn new ( name : String , ty : String , nullable : bool , default : Option < String > ) -> Self {
253
+ pub fn new (
254
+ name : String ,
255
+ ty : String ,
256
+ nullable : bool ,
257
+ default : Option < String > ,
258
+ as_ref : bool ,
259
+ ) -> Self {
253
260
Self {
254
261
name,
255
262
ty,
256
263
nullable,
257
264
default,
265
+ as_ref,
258
266
}
259
267
}
260
268
@@ -268,6 +276,7 @@ impl Arg {
268
276
match ty {
269
277
Type :: Path ( TypePath { path, .. } ) => {
270
278
let mut path = path. clone ( ) ;
279
+ let mut pass_by_ref = false ;
271
280
path. drop_lifetimes ( ) ;
272
281
273
282
let seg = path. segments . last ( ) ?;
@@ -283,25 +292,69 @@ impl Arg {
283
292
None
284
293
}
285
294
} ) ;
295
+
296
+ // For for types that are `Option<&mut T>` to turn them into `Option<&T>`, marking the Arg as
297
+ // as "passed by reference".
298
+ let option = Some ( seg)
299
+ . filter ( |seg| seg. ident == "Option" )
300
+ . and_then ( |seg| {
301
+ if let PathArguments :: AngleBracketed ( args) = & seg. arguments {
302
+ args. args
303
+ . iter ( )
304
+ . find ( |arg| matches ! ( arg, GenericArgument :: Type ( _) ) )
305
+ . map ( |ga| {
306
+ let new_ga = match ga {
307
+ GenericArgument :: Type ( ty) => {
308
+ let _rtype = match ty {
309
+ Type :: Reference ( r) => {
310
+ let mut new_ref = r. clone ( ) ;
311
+ new_ref. mutability = None ;
312
+ pass_by_ref = true ;
313
+ Type :: Reference ( new_ref)
314
+ } ,
315
+ _ => ty. clone ( ) ,
316
+ } ;
317
+ GenericArgument :: Type ( _rtype)
318
+ } ,
319
+ _ => ga. clone ( ) ,
320
+ } ;
321
+ new_ga. to_token_stream ( ) . to_string ( )
322
+ } )
323
+ } else {
324
+ None
325
+ }
326
+ } ) ;
327
+
286
328
let stringified = match result {
287
329
Some ( result) if is_return => result,
288
- _ => path. to_token_stream ( ) . to_string ( ) ,
330
+ _ => match option {
331
+ Some ( result) => result,
332
+ None => path. to_token_stream ( ) . to_string ( ) ,
333
+ } ,
289
334
} ;
290
335
291
336
Some ( Arg :: new (
292
337
name,
293
338
stringified,
294
339
seg. ident == "Option" || default. is_some ( ) ,
295
340
default,
341
+ pass_by_ref,
296
342
) )
297
343
}
298
344
Type :: Reference ( ref_) => {
299
345
// Returning references is invalid, so let's just create our arg
346
+
347
+ // Change any `&mut T` into `&T` and set the `as_ref` attribute on the Arg
348
+ // to marked it as a "passed by ref" PHP argument.
349
+ let mut ref_ = ref_. clone ( ) ;
350
+ let is_mutable_ref = ref_. mutability . is_some ( ) ;
351
+ ref_. mutability = None ;
300
352
Some ( Arg :: new (
301
353
name,
302
354
ref_. to_token_stream ( ) . to_string ( ) ,
303
355
false ,
304
356
default,
357
+ is_mutable_ref,
305
358
) )
306
359
}
307
360
_ => None ,
@@ -361,14 +414,15 @@ impl Arg {
361
414
let ty = self . get_type_ident ( ) ;
362
415
363
416
let null = self . nullable . then ( || quote ! { . allow_null( ) } ) ;
417
+ let passed_by_ref = self . as_ref . then ( || quote ! { . as_ref( ) } ) ;
364
418
let default = self . default . as_ref ( ) . map ( |val| {
365
419
quote ! {
366
420
. default ( #val)
367
421
}
368
422
} ) ;
369
423
370
424
quote ! {
371
- :: ext_php_rs:: args:: Arg :: new( #name, #ty) #null #default
425
+ :: ext_php_rs:: args:: Arg :: new( #name, #ty) #null #passed_by_ref # default
372
426
}
373
427
}
374
428
}
0 commit comments