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

davidcole1340 / ext-php-rs / 15425631619

03 Jun 2025 07:02PM CUT coverage: 20.885% (-0.04%) from 20.927%
15425631619

Pull #436

github

Xenira
chore(macro)!: change rename defaults to match psr

BREAKING CHANGE: Methods and Properties are renamed to camelCase by default. Classes to PascalCase and constants to UPPER_CASE.

Refs: #189
Pull Request #436: chore(macro)!: change rename defaults to match psr

16 of 35 new or added lines in 5 files covered. (45.71%)

2 existing lines in 2 files now uncovered.

821 of 3931 relevant lines covered (20.89%)

2.31 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, 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
    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;
×
NEW
38
    let name = attr.rename.rename(ident.to_string(), RenameRule::Pascal);
×
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 {
×
NEW
110
        self.attr
×
NEW
111
            .rename
×
NEW
112
            .rename(self.ident.to_string(), RenameRule::Camel)
×
113
    }
114
}
115

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

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

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

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

155
    let docs = quote! {
×
156
        #(#docs)*
×
157
    };
158

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

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

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

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

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

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

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

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