Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Anonymous class FromZval derive macro #78

Merged
merged 1 commit into from
Oct 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,4 +324,6 @@ const ALLOWED_BINDINGS: &[&str] = &[
"zend_std_write_property",
"zend_std_get_properties",
"zend_std_has_property",
"zend_objects_new",
"zend_standard_class_def",
];
22 changes: 22 additions & 0 deletions example/skel/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,28 @@ impl TestClass {
}
}

#[derive(Debug, ZvalConvert)]
pub struct TestStdClass<A, B, C>
where
A: PartialEq<i32>,
{
a: A,
b: B,
c: C,
}

#[derive(Debug, ZvalConvert)]
pub enum UnionExample<'a, T> {
B(T),
C(&'a str),
None,
}

#[php_function]
pub fn test_union(union: UnionExample<i32>) {
dbg!(union);
}

#[php_module]
pub fn module(module: ModuleBuilder) -> ModuleBuilder {
module
Expand Down
1 change: 0 additions & 1 deletion ext-php-rs-derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,4 @@ ident_case = "1.0.1"
quote = "1.0.9"
proc-macro2 = "1.0.26"
lazy_static = "1.4.0"
regex = "1.5"
anyhow = "1.0"
27 changes: 13 additions & 14 deletions ext-php-rs-derive/src/function.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use std::collections::HashMap;

use crate::STATE;
use crate::{syn_ext::DropLifetimes, STATE};
use anyhow::{anyhow, bail, Result};
use darling::{FromMeta, ToTokens};
use proc_macro2::{Ident, Literal, Span, TokenStream};
use quote::quote;
use regex::Regex;
use syn::{
punctuated::Punctuated, AttributeArgs, FnArg, GenericArgument, ItemFn, Lit, PathArguments,
ReturnType, Signature, Token, Type, TypePath,
Expand Down Expand Up @@ -115,7 +114,7 @@ fn build_args(
syn::Pat::Ident(pat) => pat.ident.to_string(),
_ => bail!("Invalid parameter type."),
};
Arg::from_type(&name, &ty.ty, defaults.get(&name), false)
Arg::from_type(name.clone(), &ty.ty, defaults.get(&name), false)
.ok_or_else(|| anyhow!("Invalid parameter type for parameter `{}`.", name))
}
})
Expand Down Expand Up @@ -208,33 +207,33 @@ pub fn get_return_type(output_type: &ReturnType) -> Result<Option<(String, bool)
Ok(match output_type {
ReturnType::Default => None,
ReturnType::Type(_, ty) => {
Arg::from_type("", ty, None, true).map(|arg| (arg.ty, arg.nullable))
Arg::from_type("".to_string(), ty, None, true).map(|arg| (arg.ty, arg.nullable))
}
})
}

impl Arg {
pub fn new(name: &str, ty: &str, nullable: bool, default: Option<String>) -> Self {
pub fn new(name: String, ty: String, nullable: bool, default: Option<String>) -> Self {
Self {
name: name.to_string(),
ty: Regex::new(r"'[A-Za-z]+")
.unwrap()
.replace_all(ty, "")
.to_string(),
name,
ty,
nullable,
default,
}
}

pub fn from_type(
name: &str,
name: String,
ty: &syn::Type,
default: Option<&Lit>,
is_return: bool,
) -> Option<Arg> {
let default = default.map(|lit| lit.to_token_stream().to_string());
match ty {
Type::Path(TypePath { path, .. }) => {
let mut path = path.clone();
path.drop_lifetimes();

let seg = path.segments.last()?;
let result = Some(seg)
.filter(|seg| seg.ident == "Result")
Expand All @@ -255,7 +254,7 @@ impl Arg {

Some(Arg::new(
name,
&stringified,
stringified,
seg.ident == "Option" || default.is_some(),
default,
))
Expand All @@ -264,7 +263,7 @@ impl Arg {
// Returning references is invalid, so let's just create our arg
Some(Arg::new(
name,
&ref_.to_token_stream().to_string(),
ref_.to_token_stream().to_string(),
false,
default,
))
Expand Down Expand Up @@ -361,7 +360,7 @@ impl Function {
})
.collect::<Vec<_>>();
let output = self.output.as_ref().map(|(ty, nullable)| {
let ty: Type = syn::parse_str(ty).unwrap();
let ty: Type = syn::parse_str(ty).expect("failed to parse ty");

// TODO allow reference returns?
quote! {
Expand Down
16 changes: 15 additions & 1 deletion ext-php-rs-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ mod impl_;
mod method;
mod module;
mod startup_function;
mod syn_ext;
mod zval;

use std::{
collections::HashMap,
Expand All @@ -16,7 +18,8 @@ use constant::Constant;
use proc_macro::TokenStream;
use proc_macro2::Span;
use syn::{
parse_macro_input, AttributeArgs, ItemConst, ItemFn, ItemForeignMod, ItemImpl, ItemStruct,
parse_macro_input, AttributeArgs, DeriveInput, ItemConst, ItemFn, ItemForeignMod, ItemImpl,
ItemStruct,
};

extern crate proc_macro;
Expand Down Expand Up @@ -125,3 +128,14 @@ pub fn php_extern(_: TokenStream, input: TokenStream) -> TokenStream {
}
.into()
}

#[proc_macro_derive(ZvalConvert)]
pub fn zval_convert_derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);

match zval::parser(input) {
Ok(parsed) => parsed,
Err(e) => syn::Error::new(Span::call_site(), e).to_compile_error(),
}
.into()
}
3 changes: 2 additions & 1 deletion ext-php-rs-derive/src/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,9 @@ fn build_args(
Pat::Ident(pat) => pat.ident.to_string(),
_ => bail!("Invalid parameter type."),
};
let default = defaults.get(&name);
Ok(Arg::Typed(
crate::function::Arg::from_type(&name, &ty.ty, defaults.get(&name), false)
crate::function::Arg::from_type(name.clone(), &ty.ty, default, false)
.ok_or_else(|| anyhow!("Invalid parameter type for `{}`.", name))?,
))
}
Expand Down
171 changes: 171 additions & 0 deletions ext-php-rs-derive/src/syn_ext.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
use std::mem;

/// Implemented on syn types which can contain lifetimes.
pub trait DropLifetimes {
/// Drops any lifetimes inside `self`.
fn drop_lifetimes(&mut self);
}

impl DropLifetimes for syn::Type {
fn drop_lifetimes(&mut self) {
match self {
syn::Type::Array(ty) => ty.drop_lifetimes(),
syn::Type::BareFn(ty) => ty.drop_lifetimes(),
syn::Type::Group(ty) => ty.drop_lifetimes(),
syn::Type::ImplTrait(ty) => ty.drop_lifetimes(),
syn::Type::Paren(ty) => ty.drop_lifetimes(),
syn::Type::Path(ty) => ty.drop_lifetimes(),
syn::Type::Ptr(ty) => ty.drop_lifetimes(),
syn::Type::Reference(ty) => ty.drop_lifetimes(),
syn::Type::Slice(ty) => ty.drop_lifetimes(),
syn::Type::TraitObject(ty) => ty.drop_lifetimes(),
syn::Type::Tuple(ty) => ty.drop_lifetimes(),
_ => {}
}
}
}

impl DropLifetimes for syn::TypeArray {
fn drop_lifetimes(&mut self) {
self.elem.drop_lifetimes()
}
}

impl DropLifetimes for syn::TypeBareFn {
fn drop_lifetimes(&mut self) {
self.lifetimes = None;
self.inputs.iter_mut().for_each(|i| i.drop_lifetimes());
self.output.drop_lifetimes();
}
}

impl DropLifetimes for syn::BareFnArg {
fn drop_lifetimes(&mut self) {
self.ty.drop_lifetimes();
}
}

impl DropLifetimes for syn::ReturnType {
fn drop_lifetimes(&mut self) {
if let syn::ReturnType::Type(_, t) = self {
t.drop_lifetimes();
}
}
}

impl DropLifetimes for syn::TypeGroup {
fn drop_lifetimes(&mut self) {
self.elem.drop_lifetimes()
}
}

impl DropLifetimes for syn::TypeImplTrait {
fn drop_lifetimes(&mut self) {
self.bounds.drop_lifetimes();
}
}

impl<T: Default + Clone> DropLifetimes for syn::punctuated::Punctuated<syn::TypeParamBound, T> {
fn drop_lifetimes(&mut self) {
*self = mem::take(self)
.into_iter()
.filter_map(|mut i| match &mut i {
syn::TypeParamBound::Trait(t) => {
t.drop_lifetimes();
Some(i)
}
_ => None,
})
.collect();
}
}

impl DropLifetimes for syn::TraitBound {
fn drop_lifetimes(&mut self) {
self.lifetimes = None;
self.path.drop_lifetimes();
}
}

impl DropLifetimes for syn::Path {
fn drop_lifetimes(&mut self) {
self.segments.iter_mut().for_each(|i| i.drop_lifetimes());
}
}

impl DropLifetimes for syn::PathSegment {
fn drop_lifetimes(&mut self) {
if let syn::PathArguments::AngleBracketed(args) = &mut self.arguments {
args.args = mem::take(&mut args.args)
.into_iter()
.filter_map(|mut i| {
match &mut i {
syn::GenericArgument::Type(t) => t.drop_lifetimes(),
syn::GenericArgument::Binding(t) => t.drop_lifetimes(),
syn::GenericArgument::Constraint(t) => t.drop_lifetimes(),
syn::GenericArgument::Const(_) => {}
_ => return None,
};
Some(i)
})
.collect();
}
}
}

impl DropLifetimes for syn::Binding {
fn drop_lifetimes(&mut self) {
self.ty.drop_lifetimes();
}
}

impl DropLifetimes for syn::Constraint {
fn drop_lifetimes(&mut self) {
self.bounds.drop_lifetimes();
}
}

impl DropLifetimes for syn::TypeParen {
fn drop_lifetimes(&mut self) {
self.elem.drop_lifetimes();
}
}

impl DropLifetimes for syn::TypePath {
fn drop_lifetimes(&mut self) {
if let Some(qself) = &mut self.qself {
qself.ty.drop_lifetimes();
}
}
}

impl DropLifetimes for syn::TypePtr {
fn drop_lifetimes(&mut self) {
self.elem.drop_lifetimes();
}
}

impl DropLifetimes for syn::TypeReference {
fn drop_lifetimes(&mut self) {
self.lifetime = None;
self.elem.drop_lifetimes();
}
}

impl DropLifetimes for syn::TypeSlice {
fn drop_lifetimes(&mut self) {
self.elem.drop_lifetimes();
}
}

impl DropLifetimes for syn::TypeTraitObject {
fn drop_lifetimes(&mut self) {
self.bounds.drop_lifetimes();
}
}

impl DropLifetimes for syn::TypeTuple {
fn drop_lifetimes(&mut self) {
self.elems.iter_mut().for_each(|i| i.drop_lifetimes());
}
}
Loading