Skip to content

Commit c16c5d4

Browse files
Add #[derive(ZvalConvert)] macro (#78)
1 parent 0f2cabb commit c16c5d4

File tree

14 files changed

+924
-48
lines changed

14 files changed

+924
-48
lines changed

build.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,4 +324,6 @@ const ALLOWED_BINDINGS: &[&str] = &[
324324
"zend_std_write_property",
325325
"zend_std_get_properties",
326326
"zend_std_has_property",
327+
"zend_objects_new",
328+
"zend_standard_class_def",
327329
];

example/skel/src/lib.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,28 @@ impl TestClass {
3333
}
3434
}
3535

36+
#[derive(Debug, ZvalConvert)]
37+
pub struct TestStdClass<A, B, C>
38+
where
39+
A: PartialEq<i32>,
40+
{
41+
a: A,
42+
b: B,
43+
c: C,
44+
}
45+
46+
#[derive(Debug, ZvalConvert)]
47+
pub enum UnionExample<'a, T> {
48+
B(T),
49+
C(&'a str),
50+
None,
51+
}
52+
53+
#[php_function]
54+
pub fn test_union(union: UnionExample<i32>) {
55+
dbg!(union);
56+
}
57+
3658
#[php_module]
3759
pub fn module(module: ModuleBuilder) -> ModuleBuilder {
3860
module

ext-php-rs-derive/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,4 @@ ident_case = "1.0.1"
1818
quote = "1.0.9"
1919
proc-macro2 = "1.0.26"
2020
lazy_static = "1.4.0"
21-
regex = "1.5"
2221
anyhow = "1.0"

ext-php-rs-derive/src/function.rs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
use std::collections::HashMap;
22

3-
use crate::STATE;
3+
use crate::{syn_ext::DropLifetimes, STATE};
44
use anyhow::{anyhow, bail, Result};
55
use darling::{FromMeta, ToTokens};
66
use proc_macro2::{Ident, Literal, Span, TokenStream};
77
use quote::quote;
8-
use regex::Regex;
98
use syn::{
109
punctuated::Punctuated, AttributeArgs, FnArg, GenericArgument, ItemFn, Lit, PathArguments,
1110
ReturnType, Signature, Token, Type, TypePath,
@@ -115,7 +114,7 @@ fn build_args(
115114
syn::Pat::Ident(pat) => pat.ident.to_string(),
116115
_ => bail!("Invalid parameter type."),
117116
};
118-
Arg::from_type(&name, &ty.ty, defaults.get(&name), false)
117+
Arg::from_type(name.clone(), &ty.ty, defaults.get(&name), false)
119118
.ok_or_else(|| anyhow!("Invalid parameter type for parameter `{}`.", name))
120119
}
121120
})
@@ -208,33 +207,33 @@ pub fn get_return_type(output_type: &ReturnType) -> Result<Option<(String, bool)
208207
Ok(match output_type {
209208
ReturnType::Default => None,
210209
ReturnType::Type(_, ty) => {
211-
Arg::from_type("", ty, None, true).map(|arg| (arg.ty, arg.nullable))
210+
Arg::from_type("".to_string(), ty, None, true).map(|arg| (arg.ty, arg.nullable))
212211
}
213212
})
214213
}
215214

216215
impl Arg {
217-
pub fn new(name: &str, ty: &str, nullable: bool, default: Option<String>) -> Self {
216+
pub fn new(name: String, ty: String, nullable: bool, default: Option<String>) -> Self {
218217
Self {
219-
name: name.to_string(),
220-
ty: Regex::new(r"'[A-Za-z]+")
221-
.unwrap()
222-
.replace_all(ty, "")
223-
.to_string(),
218+
name,
219+
ty,
224220
nullable,
225221
default,
226222
}
227223
}
228224

229225
pub fn from_type(
230-
name: &str,
226+
name: String,
231227
ty: &syn::Type,
232228
default: Option<&Lit>,
233229
is_return: bool,
234230
) -> Option<Arg> {
235231
let default = default.map(|lit| lit.to_token_stream().to_string());
236232
match ty {
237233
Type::Path(TypePath { path, .. }) => {
234+
let mut path = path.clone();
235+
path.drop_lifetimes();
236+
238237
let seg = path.segments.last()?;
239238
let result = Some(seg)
240239
.filter(|seg| seg.ident == "Result")
@@ -255,7 +254,7 @@ impl Arg {
255254

256255
Some(Arg::new(
257256
name,
258-
&stringified,
257+
stringified,
259258
seg.ident == "Option" || default.is_some(),
260259
default,
261260
))
@@ -264,7 +263,7 @@ impl Arg {
264263
// Returning references is invalid, so let's just create our arg
265264
Some(Arg::new(
266265
name,
267-
&ref_.to_token_stream().to_string(),
266+
ref_.to_token_stream().to_string(),
268267
false,
269268
default,
270269
))
@@ -361,7 +360,7 @@ impl Function {
361360
})
362361
.collect::<Vec<_>>();
363362
let output = self.output.as_ref().map(|(ty, nullable)| {
364-
let ty: Type = syn::parse_str(ty).unwrap();
363+
let ty: Type = syn::parse_str(ty).expect("failed to parse ty");
365364

366365
// TODO allow reference returns?
367366
quote! {

ext-php-rs-derive/src/lib.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ mod impl_;
66
mod method;
77
mod module;
88
mod startup_function;
9+
mod syn_ext;
10+
mod zval;
911

1012
use std::{
1113
collections::HashMap,
@@ -16,7 +18,8 @@ use constant::Constant;
1618
use proc_macro::TokenStream;
1719
use proc_macro2::Span;
1820
use syn::{
19-
parse_macro_input, AttributeArgs, ItemConst, ItemFn, ItemForeignMod, ItemImpl, ItemStruct,
21+
parse_macro_input, AttributeArgs, DeriveInput, ItemConst, ItemFn, ItemForeignMod, ItemImpl,
22+
ItemStruct,
2023
};
2124

2225
extern crate proc_macro;
@@ -125,3 +128,14 @@ pub fn php_extern(_: TokenStream, input: TokenStream) -> TokenStream {
125128
}
126129
.into()
127130
}
131+
132+
#[proc_macro_derive(ZvalConvert)]
133+
pub fn zval_convert_derive(input: TokenStream) -> TokenStream {
134+
let input = parse_macro_input!(input as DeriveInput);
135+
136+
match zval::parser(input) {
137+
Ok(parsed) => parsed,
138+
Err(e) => syn::Error::new(Span::call_site(), e).to_compile_error(),
139+
}
140+
.into()
141+
}

ext-php-rs-derive/src/method.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,9 @@ fn build_args(
172172
Pat::Ident(pat) => pat.ident.to_string(),
173173
_ => bail!("Invalid parameter type."),
174174
};
175+
let default = defaults.get(&name);
175176
Ok(Arg::Typed(
176-
crate::function::Arg::from_type(&name, &ty.ty, defaults.get(&name), false)
177+
crate::function::Arg::from_type(name.clone(), &ty.ty, default, false)
177178
.ok_or_else(|| anyhow!("Invalid parameter type for `{}`.", name))?,
178179
))
179180
}

ext-php-rs-derive/src/syn_ext.rs

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
use std::mem;
2+
3+
/// Implemented on syn types which can contain lifetimes.
4+
pub trait DropLifetimes {
5+
/// Drops any lifetimes inside `self`.
6+
fn drop_lifetimes(&mut self);
7+
}
8+
9+
impl DropLifetimes for syn::Type {
10+
fn drop_lifetimes(&mut self) {
11+
match self {
12+
syn::Type::Array(ty) => ty.drop_lifetimes(),
13+
syn::Type::BareFn(ty) => ty.drop_lifetimes(),
14+
syn::Type::Group(ty) => ty.drop_lifetimes(),
15+
syn::Type::ImplTrait(ty) => ty.drop_lifetimes(),
16+
syn::Type::Paren(ty) => ty.drop_lifetimes(),
17+
syn::Type::Path(ty) => ty.drop_lifetimes(),
18+
syn::Type::Ptr(ty) => ty.drop_lifetimes(),
19+
syn::Type::Reference(ty) => ty.drop_lifetimes(),
20+
syn::Type::Slice(ty) => ty.drop_lifetimes(),
21+
syn::Type::TraitObject(ty) => ty.drop_lifetimes(),
22+
syn::Type::Tuple(ty) => ty.drop_lifetimes(),
23+
_ => {}
24+
}
25+
}
26+
}
27+
28+
impl DropLifetimes for syn::TypeArray {
29+
fn drop_lifetimes(&mut self) {
30+
self.elem.drop_lifetimes()
31+
}
32+
}
33+
34+
impl DropLifetimes for syn::TypeBareFn {
35+
fn drop_lifetimes(&mut self) {
36+
self.lifetimes = None;
37+
self.inputs.iter_mut().for_each(|i| i.drop_lifetimes());
38+
self.output.drop_lifetimes();
39+
}
40+
}
41+
42+
impl DropLifetimes for syn::BareFnArg {
43+
fn drop_lifetimes(&mut self) {
44+
self.ty.drop_lifetimes();
45+
}
46+
}
47+
48+
impl DropLifetimes for syn::ReturnType {
49+
fn drop_lifetimes(&mut self) {
50+
if let syn::ReturnType::Type(_, t) = self {
51+
t.drop_lifetimes();
52+
}
53+
}
54+
}
55+
56+
impl DropLifetimes for syn::TypeGroup {
57+
fn drop_lifetimes(&mut self) {
58+
self.elem.drop_lifetimes()
59+
}
60+
}
61+
62+
impl DropLifetimes for syn::TypeImplTrait {
63+
fn drop_lifetimes(&mut self) {
64+
self.bounds.drop_lifetimes();
65+
}
66+
}
67+
68+
impl<T: Default + Clone> DropLifetimes for syn::punctuated::Punctuated<syn::TypeParamBound, T> {
69+
fn drop_lifetimes(&mut self) {
70+
*self = mem::take(self)
71+
.into_iter()
72+
.filter_map(|mut i| match &mut i {
73+
syn::TypeParamBound::Trait(t) => {
74+
t.drop_lifetimes();
75+
Some(i)
76+
}
77+
_ => None,
78+
})
79+
.collect();
80+
}
81+
}
82+
83+
impl DropLifetimes for syn::TraitBound {
84+
fn drop_lifetimes(&mut self) {
85+
self.lifetimes = None;
86+
self.path.drop_lifetimes();
87+
}
88+
}
89+
90+
impl DropLifetimes for syn::Path {
91+
fn drop_lifetimes(&mut self) {
92+
self.segments.iter_mut().for_each(|i| i.drop_lifetimes());
93+
}
94+
}
95+
96+
impl DropLifetimes for syn::PathSegment {
97+
fn drop_lifetimes(&mut self) {
98+
if let syn::PathArguments::AngleBracketed(args) = &mut self.arguments {
99+
args.args = mem::take(&mut args.args)
100+
.into_iter()
101+
.filter_map(|mut i| {
102+
match &mut i {
103+
syn::GenericArgument::Type(t) => t.drop_lifetimes(),
104+
syn::GenericArgument::Binding(t) => t.drop_lifetimes(),
105+
syn::GenericArgument::Constraint(t) => t.drop_lifetimes(),
106+
syn::GenericArgument::Const(_) => {}
107+
_ => return None,
108+
};
109+
Some(i)
110+
})
111+
.collect();
112+
}
113+
}
114+
}
115+
116+
impl DropLifetimes for syn::Binding {
117+
fn drop_lifetimes(&mut self) {
118+
self.ty.drop_lifetimes();
119+
}
120+
}
121+
122+
impl DropLifetimes for syn::Constraint {
123+
fn drop_lifetimes(&mut self) {
124+
self.bounds.drop_lifetimes();
125+
}
126+
}
127+
128+
impl DropLifetimes for syn::TypeParen {
129+
fn drop_lifetimes(&mut self) {
130+
self.elem.drop_lifetimes();
131+
}
132+
}
133+
134+
impl DropLifetimes for syn::TypePath {
135+
fn drop_lifetimes(&mut self) {
136+
if let Some(qself) = &mut self.qself {
137+
qself.ty.drop_lifetimes();
138+
}
139+
}
140+
}
141+
142+
impl DropLifetimes for syn::TypePtr {
143+
fn drop_lifetimes(&mut self) {
144+
self.elem.drop_lifetimes();
145+
}
146+
}
147+
148+
impl DropLifetimes for syn::TypeReference {
149+
fn drop_lifetimes(&mut self) {
150+
self.lifetime = None;
151+
self.elem.drop_lifetimes();
152+
}
153+
}
154+
155+
impl DropLifetimes for syn::TypeSlice {
156+
fn drop_lifetimes(&mut self) {
157+
self.elem.drop_lifetimes();
158+
}
159+
}
160+
161+
impl DropLifetimes for syn::TypeTraitObject {
162+
fn drop_lifetimes(&mut self) {
163+
self.bounds.drop_lifetimes();
164+
}
165+
}
166+
167+
impl DropLifetimes for syn::TypeTuple {
168+
fn drop_lifetimes(&mut self) {
169+
self.elems.iter_mut().for_each(|i| i.drop_lifetimes());
170+
}
171+
}

0 commit comments

Comments
 (0)