Skip to content

Commit 17b0395

Browse files
committed
feat(macro): add outer macro for classes
Refs: 335
1 parent 00e8711 commit 17b0395

File tree

5 files changed

+82
-31
lines changed

5 files changed

+82
-31
lines changed

crates/macros/src/class.rs

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use syn::{Attribute, AttributeArgs, Expr, Fields, FieldsNamed, ItemStruct, LitSt
99

1010
#[derive(Debug, Default)]
1111
pub struct Class {
12+
pub name: String,
1213
pub class_name: String,
1314
pub struct_path: String,
1415
pub parent: Option<String>,
@@ -40,7 +41,7 @@ pub struct AttrArgs {
4041
flags: Option<Expr>,
4142
}
4243

43-
pub fn parser(args: AttributeArgs, mut input: ItemStruct) -> Result<TokenStream> {
44+
pub fn parser(args: AttributeArgs, mut input: ItemStruct) -> Result<(TokenStream, Class)> {
4445
let args = AttrArgs::from_list(&args)
4546
.map_err(|e| anyhow!("Unable to parse attribute arguments: {:?}", e))?;
4647

@@ -51,7 +52,11 @@ pub fn parser(args: AttributeArgs, mut input: ItemStruct) -> Result<TokenStream>
5152

5253
input.attrs = {
5354
let mut unused = vec![];
54-
for attr in input.attrs.into_iter() {
55+
for attr in input
56+
.attrs
57+
.into_iter()
58+
.filter(|a| !a.path.is_ident("php_class"))
59+
{
5560
match parse_attribute(&attr)? {
5661
Some(parsed) => match parsed {
5762
ParsedAttribute::Extends(class) => {
@@ -120,6 +125,7 @@ pub fn parser(args: AttributeArgs, mut input: ItemStruct) -> Result<TokenStream>
120125
let struct_path = ident.to_string();
121126
let flags = args.flags.map(|flags| flags.to_token_stream().to_string());
122127
let class = Class {
128+
name: ident.to_string(),
123129
class_name,
124130
struct_path,
125131
parent,
@@ -131,23 +137,14 @@ pub fn parser(args: AttributeArgs, mut input: ItemStruct) -> Result<TokenStream>
131137
..Default::default()
132138
};
133139

134-
// let mut state = STATE.lock();
135-
//
136-
// if state.built_module {
137-
// bail!("The `#[php_module]` macro must be called last to ensure functions and classes are registered.");
138-
// }
139-
//
140-
// if state.startup_function.is_some() {
141-
// bail!("The `#[php_startup]` macro must be called after all the classes have been defined.");
142-
// }
143-
//
144-
// state.classes.insert(ident.to_string(), class);
140+
Ok((
141+
quote! {
142+
#input
145143

146-
Ok(quote! {
147-
#input
148-
149-
::ext_php_rs::class_derives!(#ident);
150-
})
144+
::ext_php_rs::class_derives!(#ident);
145+
},
146+
class,
147+
))
151148
}
152149

153150
#[derive(Debug)]

crates/macros/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ pub fn php_class(args: TokenStream, input: TokenStream) -> TokenStream {
6060
let input = parse_macro_input!(input as ItemStruct);
6161

6262
match class::parser(args, input) {
63-
Ok(parsed) => parsed,
63+
Ok((parsed, _)) => parsed,
6464
Err(e) => syn::Error::new(Span::call_site(), e).to_compile_error(),
6565
}
6666
.into()

crates/macros/src/module.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,16 @@ pub fn parser(input: ItemMod) -> Result<TokenStream> {
4646
builder.add_constant(c.clone());
4747
continue;
4848
}
49+
Item::Struct(s) => {
50+
if s.attrs
51+
.iter()
52+
.find(|a| a.path.is_ident("php_class"))
53+
.is_some()
54+
{
55+
builder.add_class(s.clone());
56+
continue;
57+
}
58+
}
4959
_ => {}
5060
}
5161
builder.add_unmapped(item.clone());

crates/macros/src/module_builder.rs

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@ use darling::FromMeta;
33
use proc_macro2::{Span, TokenStream};
44
use quote::quote;
55
use syn::{
6-
spanned::Spanned as _, Attribute, AttributeArgs, Ident, Item, ItemConst, ItemFn, NestedMeta,
6+
spanned::Spanned as _, Attribute, AttributeArgs, Ident, Item, ItemConst, ItemFn, ItemStruct,
7+
NestedMeta,
78
};
89

910
use crate::{
11+
class::{self, Class},
1012
constant::{self, Constant},
1113
function::{self, Function},
14+
module::generate_registered_class_impl,
1215
startup_function,
1316
};
1417

@@ -17,6 +20,7 @@ pub(crate) struct ModuleBuilder {
1720
pub functions: Vec<ItemFn>,
1821
pub startup_function: Option<ItemFn>,
1922
pub constants: Vec<ItemConst>,
23+
pub classes: Vec<ItemStruct>,
2024
pub unmapped: Vec<Item>,
2125
}
2226

@@ -37,27 +41,39 @@ impl ModuleBuilder {
3741
self.constants.push(constant);
3842
}
3943

44+
pub fn add_class(&mut self, class: ItemStruct) {
45+
self.classes.push(class);
46+
}
47+
4048
pub fn add_unmapped(&mut self, item: Item) {
4149
self.unmapped.push(item);
4250
}
4351

4452
pub fn build(&self) -> TokenStream {
53+
let (class_stream, classes) = self.build_classes();
4554
let (function_stream, functions) = self.build_functions();
4655
let (constant_stream, constants) = self.build_constants();
47-
let (startup_function, startup_ident) = self.build_startup_function(&constants).unwrap();
56+
let (startup_function, startup_ident) =
57+
self.build_startup_function(&classes, &constants).unwrap();
4858

4959
let functions = functions
5060
.iter()
5161
.map(|func| func.get_builder())
5262
.collect::<Vec<_>>();
63+
let registered_classes_impls = classes
64+
.iter()
65+
.map(|class| generate_registered_class_impl(class))
66+
.collect::<Result<Vec<_>, _>>()
67+
.unwrap();
5368
let unmapped = &self.unmapped;
5469

5570
// let describe_fn = generate_stubs(&state);
5671

5772
quote! {
58-
// #(#registered_classes_impls)*
5973

6074
mod module {
75+
#class_stream
76+
#(#registered_classes_impls)*
6177
#function_stream
6278
#constant_stream
6379
#startup_function
@@ -128,20 +144,47 @@ impl ModuleBuilder {
128144
)
129145
}
130146

131-
fn build_startup_function(&self, constants: &Vec<Constant>) -> Result<(TokenStream, Ident)> {
147+
fn build_classes(&self) -> (TokenStream, Vec<Class>) {
148+
let structs = self
149+
.classes
150+
.iter()
151+
.map(|class| {
152+
let args = class
153+
.attrs
154+
.iter()
155+
.find(|attr| attr.path.is_ident("php_class"));
156+
let args = parse_metadata(args.unwrap());
157+
class::parser(args, class.clone())
158+
})
159+
.collect::<Result<Vec<_>, _>>()
160+
.unwrap();
161+
162+
let tokens = structs.iter().map(|(tokens, _)| tokens);
163+
164+
(
165+
quote! { #(#tokens)* },
166+
structs.into_iter().map(|(_, c)| c).collect(),
167+
)
168+
}
169+
170+
fn build_startup_function(
171+
&self,
172+
classes: &Vec<Class>,
173+
constants: &Vec<Constant>,
174+
) -> Result<(TokenStream, Ident)> {
132175
self.startup_function
133176
.as_ref()
134177
.map(|f| {
135178
let attr = f.attrs.iter().find(|a| a.path.is_ident("php_startup"));
136179
let args = parse_attr(attr.unwrap()).unwrap();
137-
startup_function::parser(Some(args), f, constants)
180+
startup_function::parser(Some(args), f, classes, constants)
138181
})
139182
.unwrap_or_else(|| {
140183
let parsed = syn::parse2(quote! {
141184
fn php_module_startup() {}
142185
})
143186
.map_err(|_| anyhow!("Unable to generate PHP module startup function."))?;
144-
startup_function::parser(None, &parsed, constants)
187+
startup_function::parser(None, &parsed, classes, constants)
145188
})
146189
}
147190
}

crates/macros/src/startup_function.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub(crate) struct StartupArgs {
1717
pub fn parser(
1818
args: Option<StartupArgs>,
1919
input: &ItemFn,
20+
classes: &Vec<Class>,
2021
constants: &Vec<Constant>,
2122
) -> Result<(TokenStream, Ident)> {
2223
let args = args.unwrap_or_default();
@@ -25,7 +26,7 @@ pub fn parser(
2526
let Signature { ident, .. } = sig;
2627
let stmts = &block.stmts;
2728

28-
// let classes = build_classes(&state.classes)?;
29+
let classes = build_classes(classes)?;
2930
let constants = build_constants(&constants);
3031
let (before, after) = if args.before {
3132
(Some(quote! { internal(ty, module_number); }), None)
@@ -46,7 +47,7 @@ pub fn parser(
4647
::ext_php_rs::internal::ext_php_rs_startup();
4748

4849
#before
49-
// #(#classes)*
50+
#(#classes)*
5051
#(#constants)*
5152
#after
5253

@@ -58,13 +59,13 @@ pub fn parser(
5859
}
5960

6061
/// Returns a vector of `ClassBuilder`s for each class.
61-
fn build_classes(classes: &HashMap<String, Class>) -> Result<Vec<TokenStream>> {
62+
fn build_classes(classes: &Vec<Class>) -> Result<Vec<TokenStream>> {
6263
classes
6364
.iter()
64-
.map(|(name, class)| {
65+
.map(|class| {
6566
let Class { class_name, .. } = &class;
66-
let ident = Ident::new(name, Span::call_site());
67-
let meta = Ident::new(&format!("_{name}_META"), Span::call_site());
67+
let ident = Ident::new(&class.name, Span::call_site());
68+
let meta = Ident::new(&format!("_{}_META", ident), Span::call_site());
6869
let methods = class.methods.iter().map(|method| {
6970
let builder = method.get_builder(&ident);
7071
let flags = method.get_flags();

0 commit comments

Comments
 (0)