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

davidcole1340 / ext-php-rs / 18244744117

04 Oct 2025 01:08PM UTC coverage: 30.759% (+3.0%) from 27.728%
18244744117

Pull #533

github

web-flow
Merge 0936605ac into 113ef33a0
Pull Request #533: Feat/interface impl

68 of 152 new or added lines in 9 files covered. (44.74%)

3 existing lines in 2 files now uncovered.

1326 of 4311 relevant lines covered (30.76%)

7.67 hits per line

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

38.46
/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, TokenStreamExt};
5
use syn::{Attribute, Expr, Fields, ItemStruct};
6

7
use crate::helpers::get_docs;
8
use crate::parsing::{PhpRename, RenameRule};
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
    pub ce: syn::Expr,
32
    pub stub: String,
33
}
34

35
impl ToTokens for ClassEntryAttribute {
NEW
36
    fn to_tokens(&self, tokens: &mut TokenStream) {
×
NEW
37
        let ce = &self.ce;
×
NEW
38
        let stub = &self.stub;
×
NEW
39
        let token = quote! { (#ce, #stub) };
×
NEW
40
        tokens.append_all(token);
×
41
    }
42
}
43

44
pub fn parser(mut input: ItemStruct) -> Result<TokenStream> {
1✔
45
    let attr = StructAttributes::from_attributes(&input.attrs)?;
3✔
46
    let ident = &input.ident;
47
    let name = attr.rename.rename(ident.to_string(), RenameRule::Pascal);
48
    let docs = get_docs(&attr.attrs)?;
1✔
49
    input.attrs.retain(|attr| !attr.path().is_ident("php"));
9✔
50

51
    let fields = match &mut input.fields {
1✔
52
        Fields::Named(fields) => parse_fields(fields.named.iter_mut())?,
1✔
53
        _ => vec![],
×
54
    };
55

56
    let class_impl = generate_registered_class_impl(
57
        ident,
58
        &name,
59
        attr.modifier.as_ref(),
60
        attr.extends.as_ref(),
61
        &attr.implements,
62
        &fields,
63
        attr.flags.as_ref(),
64
        &docs,
65
    );
66

67
    Ok(quote! {
68
        #input
69
        #class_impl
70

71
        ::ext_php_rs::class_derives!(#ident);
72
    })
73
}
74

75
#[derive(FromAttributes, Debug, Default)]
76
#[darling(attributes(php), forward_attrs(doc), default)]
77
struct PropAttributes {
78
    prop: Flag,
79
    #[darling(flatten)]
80
    rename: PhpRename,
81
    flags: Option<Expr>,
82
    attrs: Vec<Attribute>,
83
}
84

85
fn parse_fields<'a>(fields: impl Iterator<Item = &'a mut syn::Field>) -> Result<Vec<Property<'a>>> {
1✔
86
    let mut result = vec![];
2✔
87
    for field in fields {
1✔
88
        let attr = PropAttributes::from_attributes(&field.attrs)?;
×
89
        if attr.prop.is_present() {
×
90
            let ident = field
×
91
                .ident
×
92
                .as_ref()
93
                .ok_or_else(|| err!("Only named fields can be properties."))?;
×
94
            let docs = get_docs(&attr.attrs)?;
×
95
            field.attrs.retain(|attr| !attr.path().is_ident("php"));
×
96

97
            result.push(Property { ident, attr, docs });
×
98
        }
99
    }
100

101
    Ok(result)
1✔
102
}
103

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

111
impl Property<'_> {
112
    pub fn name(&self) -> String {
×
113
        self.attr
×
114
            .rename
×
115
            .rename(self.ident.to_string(), RenameRule::Camel)
×
116
    }
117
}
118

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

133
    let fields = fields.iter().map(|prop| {
4✔
134
        let name = prop.name();
×
135
        let ident = prop.ident;
×
136
        let flags = prop
×
137
            .attr
×
138
            .flags
×
139
            .as_ref()
×
140
            .map(ToTokens::to_token_stream)
×
141
            .unwrap_or(quote! { ::ext_php_rs::flags::PropertyFlags::Public });
×
142
        let docs = &prop.docs;
×
143

144
        quote! {
×
145
            (#name, ::ext_php_rs::internal::property::PropertyInfo {
146
                prop: ::ext_php_rs::props::Property::field(|this: &mut Self| &mut this.#ident),
147
                flags: #flags,
148
                docs: &[#(#docs,)*]
149
            })
150
        }
151
    });
152

153
    let flags = match flags {
2✔
154
        Some(flags) => flags.to_token_stream(),
×
155
        None => quote! { ::ext_php_rs::flags::ClassFlags::empty() }.to_token_stream(),
1✔
156
    };
157

158
    let docs = quote! {
2✔
159
        #(#docs,)*
160
    };
161

162
    let extends = if let Some(extends) = extends {
2✔
163
        quote! {
164
            Some(#extends)
165
        }
166
    } else {
167
        quote! { None }
1✔
168
    };
169

170
    let implements = implements.iter().map(|imp| {
4✔
171
        let ce = &imp.ce;
×
172
        let stub = &imp.stub;
×
173
        quote! {
×
174
            (#ce, #stub)
175
        }
176
    });
177

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

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

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

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

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

225
            #[inline]
226
            fn constants() -> &'static [(&'static str, &'static dyn ::ext_php_rs::convert::IntoZvalDyn, &'static [&'static str])] {
227
                use ::ext_php_rs::internal::class::PhpClassImpl;
228
                ::ext_php_rs::internal::class::PhpClassImplCollector::<Self>::default().get_constants()
229
            }
230
        }
231
    }
232
}
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