Skip to content

Commit 0f7bbf3

Browse files
committed
refactor(macro): add stubs for new builder pattern
Resolves: #183
1 parent 1caa044 commit 0f7bbf3

30 files changed

+875
-851
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"
77
keywords = ["php", "ffi", "zend"]
88
version = "0.13.1"
99
authors = ["David Cole <[email protected]>"]
10-
edition = "2018"
10+
edition = "2021"
1111
categories = ["api-bindings"]
1212
exclude = ["/.github", "/.crates", "/guide"]
1313

crates/macros/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ quote = "1.0.9"
1919
proc-macro2 = "1.0.26"
2020
lazy_static = "1.4.0"
2121
anyhow = "1.0"
22+
lsp_doc_stable = "0.1.0"
2223

2324
[lints.rust]
2425
missing_docs = "warn"

crates/macros/src/class.rs

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use quote::quote;
44
use syn::parse::ParseStream;
55
use syn::{Attribute, AttributeArgs, Expr, Fields, ItemStruct, LitStr, Token};
66

7+
use crate::helpers::get_docs;
78
use crate::prelude::*;
89

910
#[derive(Debug, Default, FromMeta)]
@@ -25,7 +26,7 @@ pub struct StructArgs {
2526
struct ClassAttrs {
2627
extends: Option<syn::Expr>,
2728
implements: Vec<syn::Expr>,
28-
comments: Vec<String>,
29+
docs: Vec<String>,
2930
}
3031

3132
impl ClassAttrs {
@@ -48,16 +49,11 @@ impl ClassAttrs {
4849
Err(_) => bail!(attr => "Invalid arguments passed to implements attribute."),
4950
};
5051
self.implements.push(implements);
51-
} else if attr.path.is_ident("doc") {
52-
let comment: LitStr = match attr.parse_args() {
53-
Ok(comment) => comment,
54-
Err(_) => bail!(attr => "Invalid arguments passed to doc attribute."),
55-
};
56-
self.comments.push(comment.value());
5752
} else {
5853
attrs.push(attr);
5954
}
6055
}
56+
self.docs = get_docs(attrs);
6157
Ok(())
6258
}
6359
}
@@ -85,6 +81,7 @@ pub fn parser(args: AttributeArgs, mut input: ItemStruct) -> Result<TokenStream>
8581
&class_attrs.implements,
8682
&fields,
8783
args.flags.as_ref(),
84+
&class_attrs.docs,
8885
);
8986

9087
Ok(quote! {
@@ -225,6 +222,7 @@ pub fn parse_attribute(attr: &Attribute) -> Result<Option<ParsedAttribute>> {
225222
}
226223

227224
/// Generates an implementation of `RegisteredClass` for struct `ident`.
225+
#[allow(clippy::too_many_arguments)]
228226
fn generate_registered_class_impl(
229227
ident: &syn::Ident,
230228
class_name: Option<&str>,
@@ -233,6 +231,7 @@ fn generate_registered_class_impl(
233231
implements: &[syn::Expr],
234232
fields: &[Property],
235233
flags: Option<&syn::Expr>,
234+
docs: &[String],
236235
) -> TokenStream {
237236
let ident_str = ident.to_string();
238237
let class_name = match class_name {
@@ -241,17 +240,36 @@ fn generate_registered_class_impl(
241240
};
242241
let modifier = modifier.option_tokens();
243242
let extends = extends.option_tokens();
243+
244244
let fields = fields.iter().map(|prop| {
245245
let name = prop.name();
246246
let ident = prop.ident;
247+
let flags = prop
248+
.attr
249+
.flags
250+
.as_ref()
251+
.map(|flags| flags.to_token_stream())
252+
.unwrap_or(quote! { ::ext_php_rs::flags::PropertyFlags::Public });
253+
let docs = &prop.docs;
254+
247255
quote! {
248-
(#name, ::ext_php_rs::props::Property::field(|this: &mut Self| &mut this.#ident))
256+
(#name, ::ext_php_rs::internal::property::PropertyInfo {
257+
prop: ::ext_php_rs::props::Property::field(|this: &mut Self| &mut this.#ident),
258+
flags: #flags,
259+
docs: &[#(#docs,)*]
260+
})
249261
}
250262
});
263+
251264
let flags = match flags {
252265
Some(flags) => flags.to_token_stream(),
253266
None => quote! { ::ext_php_rs::flags::ClassFlags::empty() }.to_token_stream(),
254267
};
268+
269+
let docs = quote! {
270+
#(#docs)*
271+
};
272+
255273
quote! {
256274
impl ::ext_php_rs::class::RegisteredClass for #ident {
257275
const CLASS_NAME: &'static str = #class_name;
@@ -265,6 +283,9 @@ fn generate_registered_class_impl(
265283
#(#implements,)*
266284
];
267285
const FLAGS: ::ext_php_rs::flags::ClassFlags = #flags;
286+
const DOC_COMMENTS: &'static [&'static str] = &[
287+
#docs
288+
];
268289

269290
#[inline]
270291
fn get_metadata() -> &'static ::ext_php_rs::class::ClassMetadata<Self> {
@@ -274,7 +295,7 @@ fn generate_registered_class_impl(
274295
}
275296

276297
fn get_properties<'a>() -> ::std::collections::HashMap<
277-
&'static str, ::ext_php_rs::props::Property<'a, Self>
298+
&'static str, ::ext_php_rs::internal::property::PropertyInfo<'a, Self>
278299
> {
279300
use ::std::iter::FromIterator;
280301
::std::collections::HashMap::from_iter([
@@ -297,7 +318,7 @@ fn generate_registered_class_impl(
297318
}
298319

299320
#[inline]
300-
fn constants() -> &'static [(&'static str, &'static dyn ::ext_php_rs::convert::IntoZvalDyn)] {
321+
fn constants() -> &'static [(&'static str, &'static dyn ::ext_php_rs::convert::IntoZvalDyn, &'static [&'static str])] {
301322
use ::ext_php_rs::internal::class::PhpClassImpl;
302323
::ext_php_rs::internal::class::PhpClassImplCollector::<Self>::default().get_constants()
303324
}

crates/macros/src/constant.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use proc_macro2::TokenStream;
2+
use quote::{format_ident, quote};
3+
use syn::ItemConst;
4+
5+
use crate::helpers::get_docs;
6+
use crate::prelude::*;
7+
8+
const INTERNAL_CONST_DOC_PREFIX: &str = "_internal_const_docs_";
9+
10+
pub fn parser(item: ItemConst) -> TokenStream {
11+
let docs = get_docs(&item.attrs);
12+
let docs_ident = format_ident!("{INTERNAL_CONST_DOC_PREFIX}{}", item.ident);
13+
14+
quote! {
15+
#item
16+
#[allow(non_upper_case_globals)]
17+
const #docs_ident: &[&str] = &[#(#docs),*];
18+
}
19+
}
20+
21+
pub fn wrap(input: syn::Path) -> Result<TokenStream> {
22+
let Some(const_name) = input.get_ident().map(|i| i.to_string()) else {
23+
bail!(input => "Pass a PHP const into `wrap_constant!()`.");
24+
};
25+
let doc_const = format_ident!("{INTERNAL_CONST_DOC_PREFIX}{const_name}");
26+
27+
Ok(quote! {
28+
(#const_name, #input, #doc_const)
29+
30+
})
31+
}

crates/macros/src/function.rs

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,9 @@ pub fn wrap(input: syn::Path) -> Result<TokenStream> {
1616
bail!(input => "Pass a PHP function name into `wrap_function!()`.");
1717
};
1818
let builder_func = format_ident!("_internal_{func_name}");
19-
let err = format!("Failed to build function `{}`.", func_name);
2019

2120
Ok(quote! {{
2221
(<#builder_func as ::ext_php_rs::internal::function::PhpFunction>::FUNCTION_ENTRY)()
23-
.expect(#err)
2422
}})
2523
}
2624

@@ -165,7 +163,7 @@ impl<'a> Function<'a> {
165163
.variadic()
166164
}
167165
});
168-
let output = self.output.as_ref().map(|output| {
166+
let returns = self.output.as_ref().map(|output| {
169167
quote! {
170168
.returns(
171169
<#output as ::ext_php_rs::convert::IntoZval>::TYPE,
@@ -232,6 +230,15 @@ impl<'a> Function<'a> {
232230
}
233231
};
234232

233+
let docs = if !self.docs.is_empty() {
234+
let docs = &self.docs;
235+
quote! {
236+
.docs(&[#(#docs),*])
237+
}
238+
} else {
239+
quote! {}
240+
};
241+
235242
Ok(quote! {
236243
::ext_php_rs::builders::FunctionBuilder::new(#name, {
237244
::ext_php_rs::zend_fastcall! {
@@ -258,7 +265,8 @@ impl<'a> Function<'a> {
258265
.not_required()
259266
#(.arg(#not_required_args))*
260267
#variadic
261-
#output
268+
#returns
269+
#docs
262270
})
263271
}
264272

@@ -273,13 +281,10 @@ impl<'a> Function<'a> {
273281
struct #internal_ident;
274282

275283
impl ::ext_php_rs::internal::function::PhpFunction for #internal_ident {
276-
const FUNCTION_ENTRY: fn() -> ::ext_php_rs::error::Result<
277-
::ext_php_rs::zend::FunctionEntry
278-
> = {
279-
fn entry() -> ::ext_php_rs::error::Result<
280-
::ext_php_rs::zend::FunctionEntry
281-
> {
282-
#builder.build()
284+
const FUNCTION_ENTRY: fn() -> ::ext_php_rs::builders::FunctionBuilder<'static> = {
285+
fn entry() -> ::ext_php_rs::builders::FunctionBuilder<'static>
286+
{
287+
#builder
283288
}
284289
entry
285290
};
@@ -355,14 +360,14 @@ impl<'a> Function<'a> {
355360

356361
#[derive(Debug)]
357362
pub struct ReceiverArg {
358-
pub mutable: bool,
363+
pub _mutable: bool,
359364
pub span: Span,
360365
}
361366

362367
#[derive(Debug)]
363368
pub struct TypedArg<'a> {
364369
pub name: &'a Ident,
365-
pub ty: Box<Type>,
370+
pub ty: Type,
366371
pub nullable: bool,
367372
pub default: Option<Lit>,
368373
pub as_ref: bool,
@@ -393,7 +398,7 @@ impl<'a> Args<'a> {
393398
bail!(receiver => "Too many receivers specified.")
394399
}
395400
result.receiver.replace(ReceiverArg {
396-
mutable: receiver.mutability.is_some(),
401+
_mutable: receiver.mutability.is_some(),
397402
span: receiver.span(),
398403
});
399404
}
@@ -421,8 +426,8 @@ impl<'a> Args<'a> {
421426
Ok(result)
422427
}
423428

424-
fn parse_typed(ty: &Box<Type>) -> (bool, bool, Box<Type>) {
425-
match ty.as_ref() {
429+
fn parse_typed(ty: &Type) -> (bool, bool, Type) {
430+
match ty {
426431
Type::Reference(ref_) => {
427432
let as_ref = ref_.mutability.is_some();
428433
match ref_.elem.as_ref() {
@@ -448,7 +453,7 @@ impl<'a> Args<'a> {
448453
args.args
449454
.iter()
450455
.find(|arg| matches!(arg, GenericArgument::Type(_)))
451-
.map(|ga| match ga {
456+
.and_then(|ga| match ga {
452457
GenericArgument::Type(ty) => Some(match ty {
453458
Type::Reference(r) => {
454459
let mut new_ref = r.clone();
@@ -460,11 +465,9 @@ impl<'a> Args<'a> {
460465
}),
461466
_ => None,
462467
})
463-
.flatten()
464468
} else {
465469
None
466470
}
467-
.map(|ty| Box::new(ty))
468471
})
469472
.unwrap_or_else(|| ty.clone());
470473
(false, as_ref, ty.clone())
@@ -503,10 +506,10 @@ impl<'a> Args<'a> {
503506
}
504507
}
505508

506-
impl<'a> TypedArg<'a> {
509+
impl TypedArg<'_> {
507510
/// Returns a 'clean type' with the lifetimes removed. This allows the type
508511
/// to be used outside of the original function context.
509-
fn clean_ty(&self) -> Box<Type> {
512+
fn clean_ty(&self) -> Type {
510513
let mut ty = self.ty.clone();
511514
ty.drop_lifetimes();
512515
ty
@@ -532,17 +535,18 @@ impl<'a> TypedArg<'a> {
532535
} else {
533536
None
534537
};
535-
let default = self.default.as_ref().map(|_| {
538+
let default = self.default.as_ref().map(|val| {
539+
let val = val.to_token_stream().to_string();
536540
quote! {
537-
.default()
541+
.default(#val)
538542
}
539543
});
540544
let as_ref = if self.as_ref {
541545
Some(quote! { .as_ref() })
542546
} else {
543547
None
544548
};
545-
let variadic = self.variadic.then(|| quote! { .ir_variadic() });
549+
let variadic = self.variadic.then(|| quote! { .is_variadic() });
546550
Ok(quote! {
547551
::ext_php_rs::args::Arg::new(#name, <#ty as ::ext_php_rs::convert::FromZvalMut>::TYPE)
548552
#null
@@ -595,7 +599,7 @@ pub fn type_is_nullable(ty: &Type, has_default: bool) -> Result<bool> {
595599
.path
596600
.segments
597601
.iter()
598-
.last()
602+
.next_back()
599603
.map(|seg| seg.ident == "Option")
600604
.unwrap_or(false)
601605
}

crates/macros/src/impl_.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -342,8 +342,9 @@ impl<'a> ParsedImpl<'a> {
342342
let constants = self.constants.iter().map(|c| {
343343
let name = &c.name;
344344
let ident = c.ident;
345+
let docs = &c.docs;
345346
quote! {
346-
(#name, &#path::#ident)
347+
(#name, &#path::#ident, &[#(#docs),*])
347348
}
348349
});
349350

@@ -365,7 +366,7 @@ impl<'a> ParsedImpl<'a> {
365366
#constructor
366367
}
367368

368-
fn get_constants(self) -> &'static [(&'static str, &'static dyn ::ext_php_rs::convert::IntoZvalDyn)] {
369+
fn get_constants(self) -> &'static [(&'static str, &'static dyn ::ext_php_rs::convert::IntoZvalDyn, &'static [&'static str])] {
369370
&[#(#constants),*]
370371
}
371372
}

0 commit comments

Comments
 (0)