Skip to content

Commit

Permalink
Merge pull request #230 from davidcole1340/function-arg-pass-by-ref
Browse files Browse the repository at this point in the history
Support function args being passed by reference
  • Loading branch information
danog authored Oct 10, 2023
2 parents 7d484a0 + 64bf9e7 commit 4dd7b8d
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 3 deletions.
54 changes: 51 additions & 3 deletions crates/macros/src/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub struct Arg {
pub ty: String,
pub nullable: bool,
pub default: Option<String>,
pub as_ref: bool,
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -249,12 +250,19 @@ pub fn get_return_type(output_type: &ReturnType) -> Result<Option<(String, bool)
}

impl Arg {
pub fn new(name: String, ty: String, nullable: bool, default: Option<String>) -> Self {
pub fn new(
name: String,
ty: String,
nullable: bool,
default: Option<String>,
as_ref: bool,
) -> Self {
Self {
name,
ty,
nullable,
default,
as_ref,
}
}

Expand All @@ -268,6 +276,7 @@ impl Arg {
match ty {
Type::Path(TypePath { path, .. }) => {
let mut path = path.clone();
let mut pass_by_ref = false;
path.drop_lifetimes();

let seg = path.segments.last()?;
Expand All @@ -283,16 +292,53 @@ impl Arg {
None
}
});

// For for types that are `Option<&mut T>` to turn them into `Option<&T>`,
// marking the Arg as as "passed by reference".
let option = Some(seg)
.filter(|seg| seg.ident == "Option")
.and_then(|seg| {
if let PathArguments::AngleBracketed(args) = &seg.arguments {
args.args
.iter()
.find(|arg| matches!(arg, GenericArgument::Type(_)))
.map(|ga| {
let new_ga = match ga {
GenericArgument::Type(ty) => {
let _rtype = match ty {
Type::Reference(r) => {
let mut new_ref = r.clone();
new_ref.mutability = None;
pass_by_ref = true;
Type::Reference(new_ref)
}
_ => ty.clone(),
};
GenericArgument::Type(_rtype)
}
_ => ga.clone(),
};
new_ga.to_token_stream().to_string()
})
} else {
None
}
});

let stringified = match result {
Some(result) if is_return => result,
_ => path.to_token_stream().to_string(),
_ => match option {
Some(result) => result,
None => path.to_token_stream().to_string(),
},
};

Some(Arg::new(
name,
stringified,
seg.ident == "Option" || default.is_some(),
default,
pass_by_ref,
))
}
Type::Reference(ref_) => {
Expand All @@ -302,6 +348,7 @@ impl Arg {
ref_.to_token_stream().to_string(),
false,
default,
ref_.mutability.is_some(),
))
}
_ => None,
Expand Down Expand Up @@ -361,14 +408,15 @@ impl Arg {
let ty = self.get_type_ident();

let null = self.nullable.then(|| quote! { .allow_null() });
let passed_by_ref = self.as_ref.then(|| quote! { .as_ref() });
let default = self.default.as_ref().map(|val| {
quote! {
.default(#val)
}
});

quote! {
::ext_php_rs::args::Arg::new(#name, #ty) #null #default
::ext_php_rs::args::Arg::new(#name, #ty) #null #passed_by_ref #default
}
}
}
Expand Down
14 changes: 14 additions & 0 deletions guide/src/types/bool.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,17 @@ pub fn test_bool(input: bool) -> String {
var_dump(test_bool(true)); // string(4) "Yes!"
var_dump(test_bool(false)); // string(3) "No!"
```

## Rust example, taking by reference

```rust,no_run
# #![cfg_attr(windows, feature(abi_vectorcall))]
# extern crate ext_php_rs;
# use ext_php_rs::prelude::*;
# use ext_php_rs::types;
#[php_function]
pub fn test_bool(input: &mut types::Zval) {
input.reference_mut().unwrap().set_bool(false);
}
# fn main() {}
```

0 comments on commit 4dd7b8d

Please sign in to comment.