Skip to content

Commit 0b429bc

Browse files
committed
refactor(macro): use builder for outer macro pattern
Refs: #335
1 parent 754a57b commit 0b429bc

File tree

5 files changed

+262
-174
lines changed

5 files changed

+262
-174
lines changed

crates/macros/src/constant.rs

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,22 @@ pub struct Constant {
1313
pub value: String,
1414
}
1515

16-
pub fn parser(input: ItemConst) -> Result<TokenStream> {
17-
// let mut state = STATE.lock();
18-
//
19-
// if state.startup_function.is_some() {
20-
// bail!("Constants must be declared before you declare your startup function and module function.");
21-
// }
22-
//
23-
// state.constants.push(Constant {
24-
// name: input.ident.to_string(),
25-
// docs: get_docs(&input.attrs),
26-
// value: input.expr.to_token_stream().to_string(),
27-
// });
16+
pub fn parser(input: &mut ItemConst) -> Result<(TokenStream, Constant)> {
17+
let constant = Constant {
18+
name: input.ident.to_string(),
19+
docs: get_docs(&input.attrs),
20+
value: input.expr.to_token_stream().to_string(),
21+
};
22+
23+
input.attrs.remove(0);
2824

29-
Ok(quote! {
30-
#[allow(dead_code)]
31-
#input
32-
})
25+
Ok((
26+
quote! {
27+
#[allow(dead_code)]
28+
#input
29+
},
30+
constant,
31+
))
3332
}
3433

3534
impl Constant {

crates/macros/src/lib.rs

Lines changed: 20 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ mod helpers;
88
mod impl_;
99
mod method;
1010
mod module;
11+
mod module_builder;
1112
mod startup_function;
1213
mod syn_ext;
1314
mod zval;
1415

15-
use std::collections::HashMap;
16+
use std::{borrow::BorrowMut, collections::HashMap};
1617

1718
use constant::Constant;
1819
use darling::FromMeta;
@@ -35,65 +36,6 @@ struct State {
3536
}
3637

3738
impl State {
38-
fn parse_item(&mut self, item: &Item) -> TokenStream2 {
39-
let mut unparsed_attr = vec![];
40-
let tokens: TokenStream2 = match item {
41-
Item::Fn(func) => {
42-
for attr in &func.attrs {
43-
if attr.style == syn::AttrStyle::Outer && attr.path.is_ident("php_function") {
44-
match Self::parse_attr(attr) {
45-
Ok(attr_args) => {
46-
let (tokens, func) = function::parser(attr_args, func)
47-
.expect("Failed to parse function");
48-
self.functions.push(func);
49-
return tokens;
50-
}
51-
Err(e) => return e,
52-
}
53-
} else if attr.style == syn::AttrStyle::Outer
54-
&& attr.path.is_ident("php_startup")
55-
{
56-
match Self::parse_attr(attr) {
57-
Ok(attr_args) => {
58-
let (tokens, func) =
59-
startup_function::parser(Some(attr_args), func)
60-
.expect("Failed to parse startup function");
61-
self.startup_function = Some(func.to_string());
62-
return tokens;
63-
}
64-
Err(e) => return e,
65-
}
66-
} else {
67-
unparsed_attr.push(attr.into_token_stream());
68-
}
69-
70-
unparsed_attr.push(attr.into_token_stream());
71-
}
72-
return quote! { #func };
73-
}
74-
_ => quote! { #item },
75-
};
76-
77-
tokens
78-
}
79-
80-
fn parse_attr<T>(attr: &Attribute) -> Result<T, TokenStream2>
81-
where
82-
T: FromMeta,
83-
{
84-
let meta = Self::parse_metadata(attr);
85-
86-
Self::parse_from_meta(&meta, Some(attr.span()))
87-
}
88-
89-
fn parse_metadata(attr: &Attribute) -> Vec<NestedMeta> {
90-
if let Ok(args) = attr.parse_args::<TokenStream2>().map(|args| args.into()) {
91-
syn::parse_macro_input::parse::<AttributeArgs>(args).unwrap_or(vec![])
92-
} else {
93-
vec![]
94-
}
95-
}
96-
9739
fn parse_from_meta<T>(
9840
meta: &Vec<NestedMeta>,
9941
call_site: Option<Span>,
@@ -153,18 +95,21 @@ pub fn php_module(_: TokenStream, input: TokenStream) -> TokenStream {
15395

15496
#[proc_macro_attribute]
15597
pub fn php_startup(args: TokenStream, input: TokenStream) -> TokenStream {
156-
let args = parse_macro_input!(args as AttributeArgs);
157-
let input = parse_macro_input!(input as ItemFn);
158-
let attr_args = match State::parse_from_meta(&args, None) {
159-
Ok(attr_args) => attr_args,
160-
Err(e) => return e.into(),
161-
};
162-
163-
match startup_function::parser(Some(attr_args), &input) {
164-
Ok((parsed, _)) => parsed,
165-
Err(e) => syn::Error::new(Span::call_site(), e).to_compile_error(),
166-
}
167-
.into()
98+
// let args = parse_macro_input!(args as AttributeArgs);
99+
// let input = parse_macro_input!(input as ItemFn);
100+
// let attr_args = match State::parse_from_meta(&args, None) {
101+
// Ok(attr_args) => attr_args,
102+
// Err(e) => return e.into(),
103+
// };
104+
//
105+
// match startup_function::parser(Some(attr_args), &input) {
106+
// Ok((parsed, _)) => parsed,
107+
// Err(e) => syn::Error::new(Span::call_site(), e).to_compile_error(),
108+
// }
109+
// .into()
110+
syn::Error::new(Span::call_site(), "php_startup is not supported")
111+
.to_compile_error()
112+
.into()
168113
}
169114

170115
#[proc_macro_attribute]
@@ -181,10 +126,10 @@ pub fn php_impl(args: TokenStream, input: TokenStream) -> TokenStream {
181126

182127
#[proc_macro_attribute]
183128
pub fn php_const(_: TokenStream, input: TokenStream) -> TokenStream {
184-
let input = parse_macro_input!(input as ItemConst);
129+
let mut input = parse_macro_input!(input as ItemConst);
185130

186-
match constant::parser(input) {
187-
Ok(parsed) => parsed,
131+
match constant::parser(&mut input) {
132+
Ok((parsed, _)) => parsed,
188133
Err(e) => syn::Error::new(Span::call_site(), e).to_compile_error(),
189134
}
190135
.into()

crates/macros/src/module.rs

Lines changed: 41 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1-
use std::sync::MutexGuard;
2-
3-
use anyhow::{anyhow, bail, Result};
1+
use anyhow::Result;
42
use proc_macro2::{Ident, Span, TokenStream};
53
use quote::quote;
6-
use syn::{Item, ItemFn, ItemMod, Signature, Type};
4+
use syn::{Item, ItemMod, Type};
75

86
use crate::{
97
class::{Class, Property},
108
function::{Arg, Function},
11-
startup_function, State,
9+
module_builder::ModuleBuilder,
10+
State,
1211
};
1312

1413
pub fn parser(input: ItemMod) -> Result<TokenStream> {
@@ -20,87 +19,50 @@ pub fn parser(input: ItemMod) -> Result<TokenStream> {
2019
if input.content.is_none() {
2120
return Ok(quote! {});
2221
}
23-
let mut state = State::default();
22+
23+
let mut builder = ModuleBuilder::new();
2424
let (_, content) = &input.content.expect("module content is missing");
25-
let content = content
26-
.iter()
27-
.map(|i| state.parse_item(i))
28-
.collect::<Vec<_>>();
25+
for item in content {
26+
match item {
27+
Item::Fn(f) => {
28+
if f.attrs
29+
.iter()
30+
.find(|a| a.path.is_ident("php_startup"))
31+
.is_some()
32+
{
33+
builder.set_startup_function(f.clone());
34+
continue;
35+
} else if f
36+
.attrs
37+
.iter()
38+
.find(|a| a.path.is_ident("php_function"))
39+
.is_some()
40+
{
41+
builder.add_function(f.clone());
42+
continue;
43+
}
44+
}
45+
Item::Const(c) => {
46+
builder.add_constant(c.clone());
47+
continue;
48+
}
49+
_ => {}
50+
}
51+
builder.add_unmapped(item.clone());
52+
}
2953

3054
// let ItemFn { sig, block, .. } = input;
3155
// let Signature { output, inputs, .. } = sig;
3256
// let stmts = &block.stmts;
3357

34-
// Generate startup function if one hasn't already been tagged with the macro.
35-
let startup_fn = if (!state.classes.is_empty() || !state.constants.is_empty())
36-
&& state.startup_function.is_none()
37-
{
38-
let parsed = syn::parse2(quote! {
39-
fn php_module_startup() {}
40-
})
41-
.map_err(|_| anyhow!("Unable to generate PHP module startup function."))?;
42-
let (startup, _) = startup_function::parser(None, &parsed)?;
43-
44-
Some(startup)
45-
} else {
46-
None
47-
};
58+
// let registered_classes_impls = state
59+
// .classes
60+
// .values()
61+
// .map(generate_registered_class_impl)
62+
// .collect::<Result<Vec<_>>>()?;
63+
// let describe_fn = generate_stubs(&state);
4864

49-
let functions = state
50-
.functions
51-
.iter()
52-
.map(|func| func.get_builder())
53-
.collect::<Vec<_>>();
54-
let startup = state.startup_function.as_ref().map(|ident| {
55-
let ident = Ident::new(ident, Span::call_site());
56-
// TODO: fix module name
57-
quote! {
58-
.startup_function(module::#ident)
59-
}
60-
});
61-
let registered_classes_impls = state
62-
.classes
63-
.values()
64-
.map(generate_registered_class_impl)
65-
.collect::<Result<Vec<_>>>()?;
66-
let describe_fn = generate_stubs(&state);
67-
68-
let result = quote! {
69-
#(#registered_classes_impls)*
70-
71-
#startup_fn
72-
73-
mod module {
74-
#( #content )*
75-
}
76-
77-
#[doc(hidden)]
78-
#[no_mangle]
79-
pub extern "C" fn get_module() -> *mut ::ext_php_rs::zend::ModuleEntry {
80-
// fn internal(#inputs) #output {
81-
// #(#stmts)*
82-
// }
83-
84-
let mut builder = ::ext_php_rs::builders::ModuleBuilder::new(
85-
env!("CARGO_PKG_NAME"),
86-
env!("CARGO_PKG_VERSION")
87-
)
88-
#startup
89-
#(.function(#functions.unwrap()))*
90-
;
91-
92-
// TODO allow result return types
93-
// let builder = internal(builder);
94-
95-
match builder.build() {
96-
Ok(module) => module.into_raw(),
97-
Err(e) => panic!("Failed to build PHP module: {:?}", e),
98-
}
99-
}
100-
101-
#describe_fn
102-
};
103-
Ok(result)
65+
Ok(builder.build())
10466
}
10567

10668
/// Generates an implementation for `RegisteredClass` on the given class.

0 commit comments

Comments
 (0)