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

davidcole1340 / ext-php-rs / 14650361933

24 Apr 2025 07:46PM CUT coverage: 14.14%. Remained the same
14650361933

Pull #427

github

Xenira
docs(macro): improve `name` vs `rename` documentation

Refs: #422
Pull Request #427: docs(macro): improve `name` vs `rename` documentation

553 of 3911 relevant lines covered (14.14%)

1.3 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<syn::Expr>,
24
    #[darling(multiple)]
25
    implements: Vec<syn::Expr>,
26
    attrs: Vec<Attribute>,
27
}
28

29
pub fn parser(mut input: ItemStruct) -> Result<TokenStream> {
×
30
    let attr = StructAttributes::from_attributes(&input.attrs)?;
×
31
    let ident = &input.ident;
×
32
    let name = attr.rename.rename(ident.to_string());
×
33
    let docs = get_docs(&attr.attrs)?;
×
34
    input.attrs.retain(|attr| !attr.path().is_ident("php"));
×
35

36
    let fields = match &mut input.fields {
×
37
        Fields::Named(fields) => parse_fields(fields.named.iter_mut())?,
×
38
        _ => vec![],
×
39
    };
40

41
    let class_impl = generate_registered_class_impl(
42
        ident,
×
43
        &name,
×
44
        attr.modifier.as_ref(),
×
45
        attr.extends.as_ref(),
×
46
        &attr.implements,
×
47
        &fields,
×
48
        attr.flags.as_ref(),
×
49
        &docs,
×
50
    );
51

52
    Ok(quote! {
×
53
        #input
×
54
        #class_impl
×
55

56
        ::ext_php_rs::class_derives!(#ident);
×
57
    })
58
}
59

60
#[derive(FromAttributes, Debug, Default)]
61
#[darling(attributes(php), forward_attrs(doc), default)]
62
struct PropAttributes {
63
    prop: Flag,
64
    #[darling(flatten)]
65
    rename: PhpRename,
66
    flags: Option<Expr>,
67
    attrs: Vec<Attribute>,
68
}
69

70
fn parse_fields<'a>(fields: impl Iterator<Item = &'a mut syn::Field>) -> Result<Vec<Property<'a>>> {
×
71
    #[derive(Debug, Default, FromMeta)]
72
    #[darling(default)]
73
    struct FieldAttr {
74
        rename: Option<String>,
75
    }
76

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

88
            result.push(Property { ident, attr, docs });
×
89
        }
90
    }
91

92
    Ok(result)
×
93
}
94

95
#[derive(Debug)]
96
struct Property<'a> {
97
    pub ident: &'a syn::Ident,
98
    pub attr: PropAttributes,
99
    pub docs: Vec<String>,
100
}
101

102
impl Property<'_> {
103
    pub fn name(&self) -> String {
×
104
        self.attr.rename.rename(self.ident.to_string())
×
105
    }
106
}
107

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

123
    let fields = fields.iter().map(|prop| {
×
124
        let name = prop.name();
×
125
        let ident = prop.ident;
×
126
        let flags = prop
×
127
            .attr
×
128
            .flags
×
129
            .as_ref()
×
130
            .map(ToTokens::to_token_stream)
×
131
            .unwrap_or(quote! { ::ext_php_rs::flags::PropertyFlags::Public });
×
132
        let docs = &prop.docs;
×
133

134
        quote! {
×
135
            (#name, ::ext_php_rs::internal::property::PropertyInfo {
×
136
                prop: ::ext_php_rs::props::Property::field(|this: &mut Self| &mut this.#ident),
×
137
                flags: #flags,
×
138
                docs: &[#(#docs,)*]
×
139
            })
140
        }
141
    });
142

143
    let flags = match flags {
×
144
        Some(flags) => flags.to_token_stream(),
×
145
        None => quote! { ::ext_php_rs::flags::ClassFlags::empty() }.to_token_stream(),
×
146
    };
147

148
    let docs = quote! {
×
149
        #(#docs)*
×
150
    };
151

152
    quote! {
×
153
        impl ::ext_php_rs::class::RegisteredClass for #ident {
×
154
            const CLASS_NAME: &'static str = #class_name;
×
155
            const BUILDER_MODIFIER: ::std::option::Option<
×
156
                fn(::ext_php_rs::builders::ClassBuilder) -> ::ext_php_rs::builders::ClassBuilder
×
157
            > = #modifier;
×
158
            const EXTENDS: ::std::option::Option<
×
159
                fn() -> &'static ::ext_php_rs::zend::ClassEntry
×
160
            > = #extends;
×
161
            const IMPLEMENTS: &'static [fn() -> &'static ::ext_php_rs::zend::ClassEntry] = &[
×
162
                #(#implements,)*
×
163
            ];
164
            const FLAGS: ::ext_php_rs::flags::ClassFlags = #flags;
×
165
            const DOC_COMMENTS: &'static [&'static str] = &[
×
166
                #docs
×
167
            ];
168

169
            #[inline]
×
170
            fn get_metadata() -> &'static ::ext_php_rs::class::ClassMetadata<Self> {
×
171
                static METADATA: ::ext_php_rs::class::ClassMetadata<#ident> =
×
172
                    ::ext_php_rs::class::ClassMetadata::new();
×
173
                &METADATA
×
174
            }
175

176
            fn get_properties<'a>() -> ::std::collections::HashMap<
×
177
                &'static str, ::ext_php_rs::internal::property::PropertyInfo<'a, Self>
×
178
            > {
×
179
                use ::std::iter::FromIterator;
×
180
                ::std::collections::HashMap::from_iter([
×
181
                    #(#fields,)*
×
182
                ])
183
            }
184

185
            #[inline]
×
186
            fn method_builders() -> ::std::vec::Vec<
×
187
                (::ext_php_rs::builders::FunctionBuilder<'static>, ::ext_php_rs::flags::MethodFlags)
×
188
            > {
×
189
                use ::ext_php_rs::internal::class::PhpClassImpl;
×
190
                ::ext_php_rs::internal::class::PhpClassImplCollector::<Self>::default().get_methods()
×
191
            }
192

193
            #[inline]
×
194
            fn constructor() -> ::std::option::Option<::ext_php_rs::class::ConstructorMeta<Self>> {
×
195
                use ::ext_php_rs::internal::class::PhpClassImpl;
×
196
                ::ext_php_rs::internal::class::PhpClassImplCollector::<Self>::default().get_constructor()
×
197
            }
198

199
            #[inline]
×
200
            fn constants() -> &'static [(&'static str, &'static dyn ::ext_php_rs::convert::IntoZvalDyn, &'static [&'static str])] {
×
201
                use ::ext_php_rs::internal::class::PhpClassImpl;
×
202
                ::ext_php_rs::internal::class::PhpClassImplCollector::<Self>::default().get_constants()
×
203
            }
204
        }
205
    }
206
}
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