• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

davidcole1340 / ext-php-rs / 15618572899

12 Jun 2025 06:47PM CUT coverage: 21.105%. Remained the same
15618572899

Pull #446

github

Xenira
chore(bindings): add tooling to generate `docsrs_bindings.rs`

Refs: #443
Pull Request #446: chore(bindings): add tooling to generate `docsrs_bindings.rs`

829 of 3928 relevant lines covered (21.1%)

2.05 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/crates/macros/src/class.rs
1
use darling::util::Flag;
2
use darling::{FromAttributes, FromMeta, ToTokens};
3
use proc_macro2::TokenStream;
4
use quote::quote;
5
use syn::{Attribute, Expr, Fields, ItemStruct};
6

7
use crate::helpers::get_docs;
8
use crate::parsing::PhpRename;
9
use crate::prelude::*;
10

11
#[derive(FromAttributes, Debug, Default)]
12
#[darling(attributes(php), forward_attrs(doc), default)]
13
pub struct StructAttributes {
14
    /// The name of the PHP class. Defaults to the same name as the struct.
15
    #[darling(flatten)]
16
    rename: PhpRename,
17
    /// A modifier function which should accept one argument, a `ClassBuilder`,
18
    /// and return the same object. Allows the user to modify the class before
19
    /// it is built.
20
    modifier: Option<syn::Ident>,
21
    /// An expression of `ClassFlags` to be applied to the class.
22
    flags: Option<syn::Expr>,
23
    extends: Option<ClassEntryAttribute>,
24
    #[darling(multiple)]
25
    implements: Vec<ClassEntryAttribute>,
26
    attrs: Vec<Attribute>,
27
}
28

29
#[derive(FromMeta, Debug)]
30
pub struct ClassEntryAttribute {
31
    ce: syn::Expr,
32
    stub: String,
33
}
34

35
pub fn parser(mut input: ItemStruct) -> Result<TokenStream> {
×
36
    let attr = StructAttributes::from_attributes(&input.attrs)?;
×
37
    let ident = &input.ident;
×
38
    let name = attr.rename.rename(ident.to_string());
×
39
    let docs = get_docs(&attr.attrs)?;
×
40
    input.attrs.retain(|attr| !attr.path().is_ident("php"));
×
41

42
    let fields = match &mut input.fields {
×
43
        Fields::Named(fields) => parse_fields(fields.named.iter_mut())?,
×
44
        _ => vec![],
×
45
    };
46

47
    let class_impl = generate_registered_class_impl(
48
        ident,
×
49
        &name,
×
50
        attr.modifier.as_ref(),
×
51
        attr.extends.as_ref(),
×
52
        &attr.implements,
×
53
        &fields,
×
54
        attr.flags.as_ref(),
×
55
        &docs,
×
56
    );
57

58
    Ok(quote! {
×
59
        #input
×
60
        #class_impl
×
61

62
        ::ext_php_rs::class_derives!(#ident);
×
63
    })
64
}
65

66
#[derive(FromAttributes, Debug, Default)]
67
#[darling(attributes(php), forward_attrs(doc), default)]
68
struct PropAttributes {
69
    prop: Flag,
70
    #[darling(flatten)]
71
    rename: PhpRename,
72
    flags: Option<Expr>,
73
    attrs: Vec<Attribute>,
74
}
75

76
fn parse_fields<'a>(fields: impl Iterator<Item = &'a mut syn::Field>) -> Result<Vec<Property<'a>>> {
×
77
    #[derive(Debug, Default, FromMeta)]
78
    #[darling(default)]
79
    struct FieldAttr {
80
        rename: Option<String>,
81
    }
82

83
    let mut result = vec![];
×
84
    for field in fields {
×
85
        let attr = PropAttributes::from_attributes(&field.attrs)?;
×
86
        if attr.prop.is_present() {
×
87
            let ident = field
×
88
                .ident
×
89
                .as_ref()
90
                .ok_or_else(|| err!("Only named fields can be properties."))?;
×
91
            let docs = get_docs(&attr.attrs)?;
×
92
            field.attrs.retain(|attr| !attr.path().is_ident("php"));
×
93

94
            result.push(Property { ident, attr, docs });
×
95
        }
96
    }
97

98
    Ok(result)
×
99
}
100

101
#[derive(Debug)]
102
struct Property<'a> {
103
    pub ident: &'a syn::Ident,
104
    pub attr: PropAttributes,
105
    pub docs: Vec<String>,
106
}
107

108
impl Property<'_> {
109
    pub fn name(&self) -> String {
×
110
        self.attr.rename.rename(self.ident.to_string())
×
111
    }
112
}
113

114
/// Generates an implementation of `RegisteredClass` for struct `ident`.
115
#[allow(clippy::too_many_arguments)]
116
fn generate_registered_class_impl(
×
117
    ident: &syn::Ident,
118
    class_name: &str,
119
    modifier: Option<&syn::Ident>,
120
    extends: Option<&ClassEntryAttribute>,
121
    implements: &[ClassEntryAttribute],
122
    fields: &[Property],
123
    flags: Option<&syn::Expr>,
124
    docs: &[String],
125
) -> TokenStream {
126
    let modifier = modifier.option_tokens();
×
127

128
    let fields = fields.iter().map(|prop| {
×
129
        let name = prop.name();
×
130
        let ident = prop.ident;
×
131
        let flags = prop
×
132
            .attr
×
133
            .flags
×
134
            .as_ref()
×
135
            .map(ToTokens::to_token_stream)
×
136
            .unwrap_or(quote! { ::ext_php_rs::flags::PropertyFlags::Public });
×
137
        let docs = &prop.docs;
×
138

139
        quote! {
×
140
            (#name, ::ext_php_rs::internal::property::PropertyInfo {
×
141
                prop: ::ext_php_rs::props::Property::field(|this: &mut Self| &mut this.#ident),
×
142
                flags: #flags,
×
143
                docs: &[#(#docs,)*]
×
144
            })
145
        }
146
    });
147

148
    let flags = match flags {
×
149
        Some(flags) => flags.to_token_stream(),
×
150
        None => quote! { ::ext_php_rs::flags::ClassFlags::empty() }.to_token_stream(),
×
151
    };
152

153
    let docs = quote! {
×
154
        #(#docs)*
×
155
    };
156

157
    let extends = if let Some(extends) = extends {
×
158
        let ce = &extends.ce;
×
159
        let stub = &extends.stub;
×
160
        quote! {
×
161
            Some((#ce, #stub))
×
162
        }
163
    } else {
164
        quote! { None }
×
165
    };
166

167
    let implements = implements.iter().map(|imp| {
×
168
        let ce = &imp.ce;
×
169
        let stub = &imp.stub;
×
170
        quote! {
×
171
            (#ce, #stub)
×
172
        }
173
    });
174

175
    quote! {
×
176
        impl ::ext_php_rs::class::RegisteredClass for #ident {
×
177
            const CLASS_NAME: &'static str = #class_name;
×
178
            const BUILDER_MODIFIER: ::std::option::Option<
×
179
                fn(::ext_php_rs::builders::ClassBuilder) -> ::ext_php_rs::builders::ClassBuilder
×
180
            > = #modifier;
×
181
            const EXTENDS: ::std::option::Option<
×
182
                ::ext_php_rs::class::ClassEntryInfo
×
183
            > = #extends;
×
184
            const IMPLEMENTS: &'static [::ext_php_rs::class::ClassEntryInfo] = &[
×
185
                #(#implements,)*
×
186
            ];
187
            const FLAGS: ::ext_php_rs::flags::ClassFlags = #flags;
×
188
            const DOC_COMMENTS: &'static [&'static str] = &[
×
189
                #docs
×
190
            ];
191

192
            #[inline]
×
193
            fn get_metadata() -> &'static ::ext_php_rs::class::ClassMetadata<Self> {
×
194
                static METADATA: ::ext_php_rs::class::ClassMetadata<#ident> =
×
195
                    ::ext_php_rs::class::ClassMetadata::new();
×
196
                &METADATA
×
197
            }
198

199
            fn get_properties<'a>() -> ::std::collections::HashMap<
×
200
                &'static str, ::ext_php_rs::internal::property::PropertyInfo<'a, Self>
×
201
            > {
×
202
                use ::std::iter::FromIterator;
×
203
                ::std::collections::HashMap::from_iter([
×
204
                    #(#fields,)*
×
205
                ])
206
            }
207

208
            #[inline]
×
209
            fn method_builders() -> ::std::vec::Vec<
×
210
                (::ext_php_rs::builders::FunctionBuilder<'static>, ::ext_php_rs::flags::MethodFlags)
×
211
            > {
×
212
                use ::ext_php_rs::internal::class::PhpClassImpl;
×
213
                ::ext_php_rs::internal::class::PhpClassImplCollector::<Self>::default().get_methods()
×
214
            }
215

216
            #[inline]
×
217
            fn constructor() -> ::std::option::Option<::ext_php_rs::class::ConstructorMeta<Self>> {
×
218
                use ::ext_php_rs::internal::class::PhpClassImpl;
×
219
                ::ext_php_rs::internal::class::PhpClassImplCollector::<Self>::default().get_constructor()
×
220
            }
221

222
            #[inline]
×
223
            fn constants() -> &'static [(&'static str, &'static dyn ::ext_php_rs::convert::IntoZvalDyn, &'static [&'static str])] {
×
224
                use ::ext_php_rs::internal::class::PhpClassImpl;
×
225
                ::ext_php_rs::internal::class::PhpClassImplCollector::<Self>::default().get_constants()
×
226
            }
227
        }
228
    }
229
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc