diff --git a/crates/macros/src/class.rs b/crates/macros/src/class.rs index d13994d44..e10c36833 100644 --- a/crates/macros/src/class.rs +++ b/crates/macros/src/class.rs @@ -9,7 +9,6 @@ use syn::{Attribute, AttributeArgs, Expr, Fields, FieldsNamed, ItemStruct, LitSt #[derive(Debug, Default)] pub struct Class { - pub name: String, pub class_name: String, pub struct_path: String, pub parent: Option, @@ -41,7 +40,7 @@ pub struct AttrArgs { flags: Option, } -pub fn parser(args: AttributeArgs, mut input: ItemStruct) -> Result<(TokenStream, Class)> { +pub fn parser(args: AttributeArgs, mut input: ItemStruct) -> Result<(TokenStream, String, Class)> { let args = AttrArgs::from_list(&args) .map_err(|e| anyhow!("Unable to parse attribute arguments: {:?}", e))?; @@ -125,7 +124,6 @@ pub fn parser(args: AttributeArgs, mut input: ItemStruct) -> Result<(TokenStream let struct_path = ident.to_string(); let flags = args.flags.map(|flags| flags.to_token_stream().to_string()); let class = Class { - name: ident.to_string(), class_name, struct_path, parent, @@ -143,6 +141,7 @@ pub fn parser(args: AttributeArgs, mut input: ItemStruct) -> Result<(TokenStream ::ext_php_rs::class_derives!(#ident); }, + ident.to_string(), class, )) } diff --git a/crates/macros/src/function.rs b/crates/macros/src/function.rs index aa4f12e0d..73ac6d84c 100644 --- a/crates/macros/src/function.rs +++ b/crates/macros/src/function.rs @@ -457,9 +457,8 @@ impl Function { } }); - // TODO: fix module name quote! { - ::ext_php_rs::builders::FunctionBuilder::new(#name, module::#name_ident) + ::ext_php_rs::builders::FunctionBuilder::new(#name, #name_ident) #(#args)* #output .build() diff --git a/crates/macros/src/impl_.rs b/crates/macros/src/impl_.rs index ea74b59f5..2ddaf1484 100644 --- a/crates/macros/src/impl_.rs +++ b/crates/macros/src/impl_.rs @@ -5,6 +5,7 @@ use quote::quote; use std::collections::HashMap; use syn::{Attribute, AttributeArgs, ItemImpl, Lit, Meta, NestedMeta}; +use crate::class::Class; use crate::helpers::get_docs; use crate::{ class::{Property, PropertyAttr}, @@ -94,7 +95,11 @@ pub enum PropAttrTy { Setter, } -pub fn parser(args: AttributeArgs, input: ItemImpl) -> Result { +pub fn parser( + args: AttributeArgs, + input: ItemImpl, + classes: &mut HashMap, +) -> Result { let args = AttrArgs::from_list(&args) .map_err(|e| anyhow!("Unable to parse attribute arguments: {:?}", e))?; @@ -105,29 +110,23 @@ pub fn parser(args: AttributeArgs, input: ItemImpl) -> Result { bail!("This macro cannot be used on trait implementations."); } - // if state.startup_function.is_some() { - // bail!( - // "Impls must be declared before you declare your startup function and module function." - // ); - // } - // - // let class = state.classes.get_mut(&class_name).ok_or_else(|| { - // anyhow!( - // "You must use `#[php_class]` on the struct before using this attribute on the impl." - // ) - // })?; + let class = classes.get_mut(&class_name).ok_or_else(|| { + anyhow!( + "You must use `#[php_class]` on the struct before using this attribute on the impl." + ) + })?; let tokens = items .into_iter() .map(|item| { Ok(match item { syn::ImplItem::Const(constant) => { - // class.constants.push(Constant { - // name: constant.ident.to_string(), - // // visibility: Visibility::Public, - // docs: get_docs(&constant.attrs), - // value: constant.expr.to_token_stream().to_string(), - // }); + class.constants.push(Constant { + name: constant.ident.to_string(), + // visibility: Visibility::Public, + docs: get_docs(&constant.attrs), + value: constant.expr.to_token_stream().to_string(), + }); quote! { #[allow(dead_code)] @@ -141,24 +140,24 @@ pub fn parser(args: AttributeArgs, input: ItemImpl) -> Result { // TODO(david): How do we handle comments for getter/setter? Take the comments // from the methods?? if let Some((prop, ty)) = parsed_method.property { - // let prop = class - // .properties - // .entry(prop) - // .or_insert_with(|| Property::method(vec![], None)); + let prop = class + .properties + .entry(prop) + .or_insert_with(|| Property::method(vec![], None)); let ident = parsed_method.method.orig_ident.clone(); - // match ty { - // PropAttrTy::Getter => prop.add_getter(ident)?, - // PropAttrTy::Setter => prop.add_setter(ident)?, - // } + match ty { + PropAttrTy::Getter => prop.add_getter(ident)?, + PropAttrTy::Setter => prop.add_setter(ident)?, + } } if parsed_method.constructor { - // if class.constructor.is_some() { - // bail!("You cannot have two constructors on the same class."); - // } - // class.constructor = Some(parsed_method.method); + if class.constructor.is_some() { + bail!("You cannot have two constructors on the same class."); + } + class.constructor = Some(parsed_method.method); } else { - // class.methods.push(parsed_method.method); + class.methods.push(parsed_method.method); } parsed_method.tokens } diff --git a/crates/macros/src/lib.rs b/crates/macros/src/lib.rs index a522baf3b..ea9eaed40 100644 --- a/crates/macros/src/lib.rs +++ b/crates/macros/src/lib.rs @@ -13,16 +13,15 @@ mod startup_function; mod syn_ext; mod zval; -use std::{borrow::BorrowMut, collections::HashMap}; +use std::collections::HashMap; use constant::Constant; use darling::FromMeta; use proc_macro::TokenStream; use proc_macro2::{Span, TokenStream as TokenStream2}; -use quote::{quote, ToTokens}; use syn::{ - parse_macro_input, spanned::Spanned, Attribute, AttributeArgs, DeriveInput, Item, ItemConst, - ItemFn, ItemForeignMod, ItemImpl, ItemMod, ItemStruct, NestedMeta, + parse_macro_input, AttributeArgs, DeriveInput, ItemConst, ItemFn, ItemForeignMod, ItemMod, + NestedMeta, }; extern crate proc_macro; @@ -32,7 +31,6 @@ struct State { functions: Vec, classes: HashMap, constants: Vec, - startup_function: Option, } impl State { @@ -55,15 +53,18 @@ impl State { } #[proc_macro_attribute] -pub fn php_class(args: TokenStream, input: TokenStream) -> TokenStream { - let args = parse_macro_input!(args as AttributeArgs); - let input = parse_macro_input!(input as ItemStruct); - - match class::parser(args, input) { - Ok((parsed, _)) => parsed, - Err(e) => syn::Error::new(Span::call_site(), e).to_compile_error(), - } - .into() +pub fn php_class(_args: TokenStream, _input: TokenStream) -> TokenStream { + // let args = parse_macro_input!(args as AttributeArgs); + // let input = parse_macro_input!(input as ItemStruct); + // + // match class::parser(args, input) { + // Ok((parsed, _)) => parsed, + // Err(e) => syn::Error::new(Span::call_site(), e).to_compile_error(), + // } + // .into() + syn::Error::new(Span::call_site(), "php_class is not supported") + .to_compile_error() + .into() } #[proc_macro_attribute] @@ -114,14 +115,17 @@ pub fn php_startup(args: TokenStream, input: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn php_impl(args: TokenStream, input: TokenStream) -> TokenStream { - let args = parse_macro_input!(args as AttributeArgs); - let input = parse_macro_input!(input as ItemImpl); - - match impl_::parser(args, input) { - Ok(parsed) => parsed, - Err(e) => syn::Error::new(Span::call_site(), e).to_compile_error(), - } - .into() + // let args = parse_macro_input!(args as AttributeArgs); + // let input = parse_macro_input!(input as ItemImpl); + // + // match impl_::parser(args, input) { + // Ok(parsed) => parsed, + // Err(e) => syn::Error::new(Span::call_site(), e).to_compile_error(), + // } + // .into() + syn::Error::new(Span::call_site(), "php_impl is not supported") + .to_compile_error() + .into() } #[proc_macro_attribute] diff --git a/crates/macros/src/module.rs b/crates/macros/src/module.rs index 6036072e4..5a3ef672f 100644 --- a/crates/macros/src/module.rs +++ b/crates/macros/src/module.rs @@ -56,6 +56,16 @@ pub fn parser(input: ItemMod) -> Result { continue; } } + Item::Impl(i) => { + if i.attrs + .iter() + .find(|a| a.path.is_ident("php_impl")) + .is_some() + { + builder.add_implementation(i.clone()); + continue; + } + } _ => {} } builder.add_unmapped(item.clone()); diff --git a/crates/macros/src/module_builder.rs b/crates/macros/src/module_builder.rs index 0fea153c7..23b068d3e 100644 --- a/crates/macros/src/module_builder.rs +++ b/crates/macros/src/module_builder.rs @@ -1,16 +1,19 @@ +use std::{collections::HashMap, iter::Map}; + use anyhow::{anyhow, Result}; use darling::FromMeta; use proc_macro2::{Span, TokenStream}; use quote::quote; use syn::{ - spanned::Spanned as _, Attribute, AttributeArgs, Ident, Item, ItemConst, ItemFn, ItemStruct, - NestedMeta, + spanned::Spanned as _, Attribute, AttributeArgs, Ident, Item, ItemConst, ItemFn, ItemImpl, + ItemStruct, NestedMeta, }; use crate::{ class::{self, Class}, constant::{self, Constant}, function::{self, Function}, + impl_, module::generate_registered_class_impl, startup_function, }; @@ -21,6 +24,7 @@ pub(crate) struct ModuleBuilder { pub startup_function: Option, pub constants: Vec, pub classes: Vec, + pub implementations: Vec, pub unmapped: Vec, } @@ -45,12 +49,30 @@ impl ModuleBuilder { self.classes.push(class); } + pub fn add_implementation(&mut self, implementation: ItemImpl) { + self.implementations.push(implementation); + } + pub fn add_unmapped(&mut self, item: Item) { self.unmapped.push(item); } pub fn build(&self) -> TokenStream { - let (class_stream, classes) = self.build_classes(); + let (class_stream, mut classes) = self.build_classes(); + let impl_stream = &self + .implementations + .iter() + .map(|implementation| { + let args = implementation + .attrs + .iter() + .find(|attr| attr.path.is_ident("php_impl")); + let args = parse_metadata(args.unwrap()); + impl_::parser(args, implementation.clone(), &mut classes) + }) + .collect::, _>>() + .unwrap(); + let (function_stream, functions) = self.build_functions(); let (constant_stream, constants) = self.build_constants(); let (startup_function, startup_ident) = @@ -62,7 +84,7 @@ impl ModuleBuilder { .collect::>(); let registered_classes_impls = classes .iter() - .map(|class| generate_registered_class_impl(class)) + .map(|(_, class)| generate_registered_class_impl(class)) .collect::, _>>() .unwrap(); let unmapped = &self.unmapped; @@ -70,40 +92,39 @@ impl ModuleBuilder { // let describe_fn = generate_stubs(&state); quote! { - mod module { #class_stream + #(#impl_stream)* #(#registered_classes_impls)* #function_stream #constant_stream #startup_function #(#unmapped)* - } - #[doc(hidden)] - #[no_mangle] - pub extern "C" fn get_module() -> *mut ::ext_php_rs::zend::ModuleEntry { - // fn internal(#inputs) #output { - // #(#stmts)* - // } - - let mut builder = ::ext_php_rs::builders::ModuleBuilder::new( - env!("CARGO_PKG_NAME"), - env!("CARGO_PKG_VERSION") - ) - .startup_function(module::#startup_ident) - #(.function(#functions.unwrap()))* - ; - - // TODO allow result return types - // let builder = internal(builder); - - match builder.build() { - Ok(module) => module.into_raw(), - Err(e) => panic!("Failed to build PHP module: {:?}", e), + #[doc(hidden)] + #[no_mangle] + pub extern "C" fn get_module() -> *mut ::ext_php_rs::zend::ModuleEntry { + // fn internal(#inputs) #output { + // #(#stmts)* + // } + + let mut builder = ::ext_php_rs::builders::ModuleBuilder::new( + env!("CARGO_PKG_NAME"), + env!("CARGO_PKG_VERSION") + ) + .startup_function(#startup_ident) + #(.function(#functions.unwrap()))* + ; + + // TODO allow result return types + // let builder = internal(builder); + + match builder.build() { + Ok(module) => module.into_raw(), + Err(e) => panic!("Failed to build PHP module: {:?}", e), + } } } - // #describe_fn } } @@ -144,7 +165,7 @@ impl ModuleBuilder { ) } - fn build_classes(&self) -> (TokenStream, Vec) { + fn build_classes(&self) -> (TokenStream, HashMap) { let structs = self .classes .iter() @@ -159,17 +180,20 @@ impl ModuleBuilder { .collect::, _>>() .unwrap(); - let tokens = structs.iter().map(|(tokens, _)| tokens); + let tokens = structs.iter().map(|(tokens, _, _)| tokens); ( quote! { #(#tokens)* }, - structs.into_iter().map(|(_, c)| c).collect(), + structs + .into_iter() + .map(|(_, name, class)| (name, class)) + .collect(), ) } fn build_startup_function( &self, - classes: &Vec, + classes: &HashMap, constants: &Vec, ) -> Result<(TokenStream, Ident)> { self.startup_function diff --git a/crates/macros/src/startup_function.rs b/crates/macros/src/startup_function.rs index 0f950392b..c8fbf8765 100644 --- a/crates/macros/src/startup_function.rs +++ b/crates/macros/src/startup_function.rs @@ -17,7 +17,7 @@ pub(crate) struct StartupArgs { pub fn parser( args: Option, input: &ItemFn, - classes: &Vec, + classes: &HashMap, constants: &Vec, ) -> Result<(TokenStream, Ident)> { let args = args.unwrap_or_default(); @@ -59,12 +59,12 @@ pub fn parser( } /// Returns a vector of `ClassBuilder`s for each class. -fn build_classes(classes: &Vec) -> Result> { +fn build_classes(classes: &HashMap) -> Result> { classes .iter() - .map(|class| { + .map(|(name, class)| { let Class { class_name, .. } = &class; - let ident = Ident::new(&class.name, Span::call_site()); + let ident = Ident::new(&name, Span::call_site()); let meta = Ident::new(&format!("_{}_META", ident), Span::call_site()); let methods = class.methods.iter().map(|method| { let builder = method.get_builder(&ident);