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

facet-rs / facet / 14933303211

09 May 2025 04:23PM UTC coverage: 55.263% (-3.5%) from 58.76%
14933303211

push

github

fasterthanlime
Uncomment facet-bench

6657 of 12046 relevant lines covered (55.26%)

73.56 hits per line

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

90.24
/facet-derive-emit/src/process_struct.rs
1
use super::*;
2
use quote::{format_ident, quote};
3

4
/// Generates the `::facet::Field` definition `TokenStream` from a `PStructField`.
5
pub(crate) fn gen_field_from_pfield(
492✔
6
    field: &PStructField,
492✔
7
    struct_name: &Ident,
492✔
8
    bgp: &BoundedGenericParams,
492✔
9
    base_offset: Option<TokenStream>,
492✔
10
) -> TokenStream {
492✔
11
    let field_name_effective = &field.name.effective;
492✔
12
    let field_name_raw = &field.name.raw;
492✔
13
    let field_type = &field.ty; // TokenStream of the type
492✔
14

492✔
15
    let bgp_without_bounds = bgp.display_without_bounds();
492✔
16

492✔
17
    // Determine field flags and other attributes from field.attrs
492✔
18
    let mut flags = quote! {};
492✔
19
    let mut flags_empty = true;
492✔
20

492✔
21
    let mut vtable_items: Vec<TokenStream> = vec![];
492✔
22
    let mut attribute_list: Vec<TokenStream> = vec![];
492✔
23
    let doc_lines: Vec<TokenStream> = field.attrs.doc.iter().map(|doc| quote!(#doc)).collect();
492✔
24
    let mut shape_of = quote! { shape_of };
492✔
25
    let mut asserts: Vec<TokenStream> = vec![];
492✔
26

27
    // Process attributes other than rename rules, which are handled by PName
28
    for attr in &field.attrs.facet {
521✔
29
        match attr {
29✔
30
            PFacetAttr::Sensitive => {
31
                if flags_empty {
8✔
32
                    flags_empty = false;
7✔
33
                    flags = quote! { ::facet::FieldFlags::SENSITIVE };
7✔
34
                } else {
7✔
35
                    flags = quote! { #flags.union(::facet::FieldFlags::SENSITIVE) };
1✔
36
                }
1✔
37
            }
38
            PFacetAttr::Default => {
39
                if flags_empty {
2✔
40
                    flags_empty = false;
2✔
41
                    flags = quote! { ::facet::FieldFlags::DEFAULT };
2✔
42
                } else {
2✔
43
                    flags = quote! { #flags.union(::facet::FieldFlags::DEFAULT) };
×
44
                }
×
45
                asserts.push(quote! {
2✔
46
                    ::facet::static_assertions::assert_impl_all!(#field_type: ::core::default::Default);
47
                })
48
            }
49
            PFacetAttr::DefaultEquals { expr } => {
4✔
50
                if flags_empty {
4✔
51
                    flags_empty = false;
4✔
52
                    flags = quote! { ::facet::FieldFlags::DEFAULT };
4✔
53
                } else {
4✔
54
                    flags = quote! { #flags.union(::facet::FieldFlags::DEFAULT) };
×
55
                }
×
56

57
                vtable_items.push(quote! {
4✔
58
                    .default_fn(|ptr| {
59
                        unsafe { ptr.put::<#field_type>(#expr) }
60
                    })
61
                });
62
            }
63
            PFacetAttr::Child => {
64
                if flags_empty {
×
65
                    flags_empty = false;
×
66
                    flags = quote! { ::facet::FieldFlags::CHILD };
×
67
                } else {
×
68
                    flags = quote! { #flags.union(::facet::FieldFlags::CHILD) };
×
69
                }
×
70
            }
71
            PFacetAttr::Flatten => {
72
                if flags_empty {
3✔
73
                    flags_empty = false;
3✔
74
                    flags = quote! { ::facet::FieldFlags::FLATTEN };
3✔
75
                } else {
3✔
76
                    flags = quote! { #flags.union(::facet::FieldFlags::FLATTEN) };
×
77
                }
×
78
            }
79
            PFacetAttr::Opaque => {
3✔
80
                shape_of = quote! { shape_of_opaque };
3✔
81
            }
3✔
82
            PFacetAttr::Arbitrary { content } => {
4✔
83
                attribute_list.push(quote! { ::facet::FieldAttribute::Arbitrary(#content) });
4✔
84
            }
4✔
85
            PFacetAttr::SkipSerializing => {
86
                if flags_empty {
3✔
87
                    flags_empty = false;
3✔
88
                    flags = quote! { ::facet::FieldFlags::SKIP_SERIALIZING };
3✔
89
                } else {
3✔
90
                    flags = quote! { #flags.union(::facet::FieldFlags::SKIP_SERIALIZING) };
×
91
                }
×
92
            }
93
            PFacetAttr::SkipSerializingIf { expr } => {
2✔
94
                let predicate = expr;
2✔
95
                let field_ty = field_type;
2✔
96
                vtable_items.push(quote! {
2✔
97
                    .skip_serializing_if(unsafe { ::std::mem::transmute((#predicate) as fn(&#field_ty) -> bool) })
2✔
98
                });
2✔
99
            }
2✔
100
            // These are handled by PName or are container-level, so ignore them for field attributes.
101
            PFacetAttr::RenameAll { .. } => {} // Explicitly ignore rename attributes here
×
102
            PFacetAttr::Transparent
103
            | PFacetAttr::Invariants { .. }
104
            | PFacetAttr::DenyUnknownFields => {}
×
105
        }
106
    }
107

108
    let maybe_attributes = if attribute_list.is_empty() {
492✔
109
        quote! {}
489✔
110
    } else {
111
        quote! { .attributes(&const { [#(#attribute_list),*] }) }
3✔
112
    };
113

114
    let maybe_field_doc = if doc_lines.is_empty() {
492✔
115
        quote! {}
466✔
116
    } else {
117
        quote! { .doc(&[#(#doc_lines),*]) }
26✔
118
    };
119

120
    let maybe_vtable = if vtable_items.is_empty() {
492✔
121
        quote! {}
486✔
122
    } else {
123
        quote! {
6✔
124
            .vtable(&const {
125
                ::facet::FieldVTable::builder()
126
                    #(#vtable_items)*
127
                    .build()
128
            })
129
        }
130
    };
131

132
    let maybe_flags = if flags_empty {
492✔
133
        quote! {}
473✔
134
    } else {
135
        quote! { .flags(#flags) }
19✔
136
    };
137

138
    // Calculate the final offset, incorporating the base_offset if present
139
    let final_offset = match base_offset {
492✔
140
        Some(base) => {
60✔
141
            quote! { #base + ::core::mem::offset_of!(#struct_name #bgp_without_bounds, #field_name_raw) }
60✔
142
        }
143
        None => {
144
            quote! { ::core::mem::offset_of!(#struct_name #bgp_without_bounds, #field_name_raw) }
432✔
145
        }
146
    };
147

148
    quote! {
492✔
149
        {
150
            #(#asserts)*;
151
            ::facet::Field::builder()
152
                // Use the effective name (after rename rules) for metadata
153
                .name(#field_name_effective)
154
                // Use the raw field name/index TokenStream for shape_of and offset_of
155
                .shape(::facet::#shape_of(&|s: &#struct_name #bgp_without_bounds| &s.#field_name_raw))
156
                .offset(#final_offset)
157
                #maybe_flags
158
                #maybe_attributes
159
                #maybe_field_doc
160
                #maybe_vtable
161
                .build()
162
        }
163
    }
164
}
492✔
165

166
/// Processes a regular struct to implement Facet
167
///
168
/// Example input:
169
/// ```rust
170
/// struct Blah {
171
///     foo: u32,
172
///     bar: String,
173
/// }
174
/// ```
175
pub(crate) fn process_struct(parsed: Struct) -> TokenStream {
191✔
176
    let ps = PStruct::parse(&parsed); // Use the parsed representation
191✔
177

191✔
178
    let struct_name_ident = format_ident!("{}", ps.container.name);
191✔
179
    let struct_name = &ps.container.name;
191✔
180
    let struct_name_str = struct_name.to_string();
191✔
181

182
    // Use PStruct for kind and fields
183
    let (kind, fields_vec) = match &ps.kind {
191✔
184
        PStructKind::Struct { fields } => {
155✔
185
            let kind = quote!(::facet::StructKind::Struct);
155✔
186
            let fields_vec = fields
155✔
187
                .iter()
155✔
188
                .map(|field| gen_field_from_pfield(field, struct_name, &ps.container.bgp, None))
280✔
189
                .collect::<Vec<_>>();
155✔
190
            (kind, fields_vec)
155✔
191
        }
192
        PStructKind::TupleStruct { fields } => {
31✔
193
            let kind = quote!(::facet::StructKind::TupleStruct);
31✔
194
            let fields_vec = fields
31✔
195
                .iter()
31✔
196
                .map(|field| gen_field_from_pfield(field, struct_name, &ps.container.bgp, None))
50✔
197
                .collect::<Vec<_>>();
31✔
198
            (kind, fields_vec)
31✔
199
        }
200
        PStructKind::UnitStruct => {
201
            let kind = quote!(::facet::StructKind::Unit);
5✔
202
            (kind, vec![])
5✔
203
        }
204
    };
205

206
    // Still need original AST for where clauses and type params for build_ helpers
207
    let where_clauses_ast = match &parsed.kind {
191✔
208
        StructKind::Struct { clauses, .. } => clauses.as_ref(),
155✔
209
        StructKind::TupleStruct { clauses, .. } => clauses.as_ref(),
31✔
210
        StructKind::UnitStruct { clauses, .. } => clauses.as_ref(),
5✔
211
    };
212
    let where_clauses = build_where_clauses(where_clauses_ast, parsed.generics.as_ref());
191✔
213
    let type_params = build_type_params(parsed.generics.as_ref());
191✔
214

215
    // Static decl using PStruct BGP
216
    let static_decl = if ps.container.bgp.params.is_empty() {
191✔
217
        generate_static_decl(struct_name)
168✔
218
    } else {
219
        TokenStream::new()
23✔
220
    };
221

222
    // Doc comments from PStruct
223
    let maybe_container_doc = if ps.container.attrs.doc.is_empty() {
191✔
224
        quote! {}
176✔
225
    } else {
226
        let doc_lines = ps.container.attrs.doc.iter().map(|s| quote!(#s));
19✔
227
        quote! { .doc(&[#(#doc_lines),*]) }
15✔
228
    };
229

230
    // Container attributes from PStruct
231
    let container_attributes_tokens = {
191✔
232
        let mut items = Vec::new();
191✔
233
        for attr in &ps.container.attrs.facet {
212✔
234
            match attr {
21✔
235
                PFacetAttr::DenyUnknownFields => {
3✔
236
                    items.push(quote! { ::facet::ShapeAttribute::DenyUnknownFields });
3✔
237
                }
3✔
238
                PFacetAttr::Default | PFacetAttr::DefaultEquals { .. } => {
2✔
239
                    // Corresponds to `#[facet(default)]` on container
2✔
240
                    items.push(quote! { ::facet::ShapeAttribute::Default });
2✔
241
                }
2✔
242
                PFacetAttr::Transparent => {
3✔
243
                    items.push(quote! { ::facet::ShapeAttribute::Transparent });
3✔
244
                }
3✔
245
                PFacetAttr::RenameAll { .. } => {}
10✔
246
                PFacetAttr::Arbitrary { content } => {
2✔
247
                    items.push(quote! { ::facet::ShapeAttribute::Arbitrary(#content) });
2✔
248
                }
2✔
249
                // Others not applicable at container level or handled elsewhere
250
                PFacetAttr::Sensitive
251
                | PFacetAttr::Opaque
252
                | PFacetAttr::Invariants { .. }
253
                | PFacetAttr::SkipSerializing
254
                | PFacetAttr::SkipSerializingIf { .. }
255
                | PFacetAttr::Flatten
256
                | PFacetAttr::Child => {}
1✔
257
            }
258
        }
259
        if items.is_empty() {
191✔
260
            quote! {}
183✔
261
        } else {
262
            quote! { .attributes(&[#(#items),*]) }
8✔
263
        }
264
    };
265

266
    // Invariants from PStruct
267
    let invariant_maybe = {
191✔
268
        let mut invariant_fns = Vec::new();
191✔
269
        for attr in &ps.container.attrs.facet {
212✔
270
            if let PFacetAttr::Invariants { expr } = attr {
21✔
271
                invariant_fns.push(expr);
1✔
272
            }
20✔
273
        }
274

275
        if !invariant_fns.is_empty() {
191✔
276
            let tests = invariant_fns.iter().map(|expr| {
1✔
277
                quote! {
1✔
278
                    if !#expr(value) {
1✔
279
                        return false;
1✔
280
                    }
1✔
281
                }
1✔
282
            });
1✔
283

284
            let bgp_display = ps.container.bgp.display_without_bounds(); // Use the BGP from PStruct
1✔
285
            quote! {
1✔
286
                unsafe fn invariants<'mem>(value: ::facet::PtrConst<'mem>) -> bool {
287
                    let value = value.get::<#struct_name_ident #bgp_display>();
288
                    #(#tests)*
289
                    true
290
                }
291

292
                vtable.invariants = Some(invariants);
293
            }
294
        } else {
295
            quote! {}
190✔
296
        }
297
    };
298

299
    // Transparent logic using PStruct
300
    let inner_field = if ps.container.attrs.is_transparent() {
191✔
301
        match &ps.kind {
3✔
302
            PStructKind::TupleStruct { fields } => {
3✔
303
                if fields.len() > 1 {
3✔
304
                    return quote! {
×
305
                        compile_error!("Transparent structs must be tuple structs with zero or one field");
306
                    };
307
                }
3✔
308
                fields.first().cloned() // Use first field if it exists, None otherwise (ZST case)
3✔
309
            }
310
            _ => {
311
                return quote! {
×
312
                    compile_error!("Transparent structs must be tuple structs");
313
                };
314
            }
315
        }
316
    } else {
317
        None
188✔
318
    };
319

320
    // Add try_from_inner implementation for transparent types
321
    let try_from_inner_code = if ps.container.attrs.is_transparent() {
191✔
322
        if let Some(inner_field) = &inner_field {
3✔
323
            // Transparent struct with one field
324
            let inner_field_type = &inner_field.ty;
3✔
325
            let bgp_without_bounds = ps.container.bgp.display_without_bounds();
3✔
326

3✔
327
            quote! {
3✔
328
                // Define the try_from function for the value vtable
329
                unsafe fn try_from<'src, 'dst>(
330
                    src_ptr: ::facet::PtrConst<'src>,
331
                    src_shape: &'static ::facet::Shape,
332
                    dst: ::facet::PtrUninit<'dst>
333
                ) -> Result<::facet::PtrMut<'dst>, ::facet::TryFromError> {
334
                    match <#inner_field_type as ::facet::Facet>::SHAPE.vtable.try_from {
335
                        Some(inner_try) => unsafe { (inner_try)(src_ptr, src_shape, dst) },
336
                        None => {
337
                            // Otherwise, check if source shape is exactly the inner shape
338
                            if src_shape != <#inner_field_type as ::facet::Facet>::SHAPE {
339
                                return Err(::facet::TryFromError::UnsupportedSourceShape {
340
                                    src_shape,
341
                                    expected: const { &[ &<#inner_field_type as ::facet::Facet>::SHAPE ] },
342
                                });
343
                            }
344
                            // Read the inner value and construct the wrapper.
345
                            let inner: #inner_field_type = unsafe { src_ptr.read() };
346
                            Ok(unsafe { dst.put(inner) }) // Construct wrapper
347
                        }
348
                    }
349
                }
350
                vtable.try_from = Some(try_from);
351

352
                // Define the try_into_inner function for the value vtable
353
                unsafe fn try_into_inner<'src, 'dst>(
354
                    src_ptr: ::facet::PtrConst<'src>,
355
                    dst: ::facet::PtrUninit<'dst>
356
                ) -> Result<::facet::PtrMut<'dst>, ::facet::TryIntoInnerError> {
357
                    let wrapper = unsafe { src_ptr.get::<#struct_name_ident #bgp_without_bounds>() };
358
                    Ok(unsafe { dst.put(wrapper.0.clone()) }) // Assume tuple struct field 0
359
                }
360
                vtable.try_into_inner = Some(try_into_inner);
361

362
                // Define the try_borrow_inner function for the value vtable
363
                unsafe fn try_borrow_inner<'src>(
364
                    src_ptr: ::facet::PtrConst<'src>
365
                ) -> Result<::facet::PtrConst<'src>, ::facet::TryBorrowInnerError> {
366
                    // Get the wrapper value
367
                    let wrapper = unsafe { src_ptr.get::<#struct_name_ident #bgp_without_bounds>() };
368
                    // Return a pointer to the inner value (field 0)
369
                    Ok(::facet::PtrConst::new(&wrapper.0 as *const _ as *const u8))
370
                }
371
                vtable.try_borrow_inner = Some(try_borrow_inner);
372
            }
373
        } else {
374
            // Transparent ZST struct (like struct Unit;)
375
            quote! {
×
376
                // Define the try_from function for the value vtable (ZST case)
377
                unsafe fn try_from<'src, 'dst>(
378
                     src_ptr: ::facet::PtrConst<'src>,
379
                     src_shape: &'static ::facet::Shape,
380
                     dst: ::facet::PtrUninit<'dst>
381
                ) -> Result<::facet::PtrMut<'dst>, ::facet::TryFromError> {
382
                     if src_shape.layout.size() == 0 {
383
                         Ok(unsafe { dst.put(#struct_name_ident) }) // Construct ZST
384
                     } else {
385
                         Err(::facet::TryFromError::UnsupportedSourceShape {
386
                             src_shape,
387
                             expected: const { &[ <() as ::facet::Facet>::SHAPE ] }, // Expect unit-like shape
388
                         })
389
                     }
390
                }
391
                 vtable.try_from = Some(try_from);
392

393
                // ZSTs cannot be meaningfully borrowed or converted *into* an inner value
394
                // try_into_inner and try_borrow_inner remain None
395
            }
396
        }
397
    } else {
398
        quote! {} // Not transparent
188✔
399
    };
400

401
    // Generate the inner shape function for transparent types
402
    let inner_shape_fn = if ps.container.attrs.is_transparent() {
191✔
403
        if let Some(inner_field) = &inner_field {
3✔
404
            let ty = &inner_field.ty;
3✔
405
            quote! {
3✔
406
                // Function to return inner type's shape
407
                fn inner_shape() -> &'static ::facet::Shape {
408
                    <#ty as ::facet::Facet>::SHAPE
409
                }
410
            }
411
        } else {
412
            // Transparent ZST case
413
            quote! {
×
414
                fn inner_shape() -> &'static ::facet::Shape {
415
                    <() as ::facet::Facet>::SHAPE // Inner shape is unit
416
                }
417
            }
418
        }
419
    } else {
420
        quote! {}
188✔
421
    };
422

423
    let inner_setter = if ps.container.attrs.is_transparent() {
191✔
424
        quote! { .inner(inner_shape) }
3✔
425
    } else {
426
        quote! {}
188✔
427
    };
428

429
    // Generics from PStruct
430
    let facet_bgp = ps
191✔
431
        .container
191✔
432
        .bgp
191✔
433
        .with_lifetime(LifetimeName(format_ident!("__facet")));
191✔
434
    let bgp_def = facet_bgp.display_with_bounds();
191✔
435
    let bgp_without_bounds = ps.container.bgp.display_without_bounds();
191✔
436

437
    // Final quote block using refactored parts
438
    let result = quote! {
191✔
439
        #static_decl
440

441
        #[automatically_derived]
442
        unsafe impl #bgp_def ::facet::Facet<'__facet> for #struct_name_ident #bgp_without_bounds #where_clauses {
443
            const VTABLE: &'static ::facet::ValueVTable = &const {
444
                let mut vtable = ::facet::value_vtable!(
445
                    Self,
446
                    |f, _opts| ::core::fmt::Write::write_str(f, #struct_name_str)
447
                );
448
                #invariant_maybe
449
                #try_from_inner_code // Use the generated code for transparent types
450
                vtable
451
            };
452

453
            const SHAPE: &'static ::facet::Shape = &const {
454
                let fields: &'static [::facet::Field] = &const {[#(#fields_vec),*]};
455

456
                #inner_shape_fn // Include inner_shape function if needed
457

458
                ::facet::Shape::builder_for_sized::<Self>()
459
                    #type_params // Still from parsed.generics
460
                    .ty(::facet::Type::User(::facet::UserType::Struct(::facet::StructType::builder()
461
                        .repr(::facet::Repr::c())
462
                        .kind(#kind)
463
                        .fields(fields)
464
                        .build()
465
                    )))
466
                    #inner_setter // Use transparency flag from PStruct
467
                    #maybe_container_doc // From ps.container.attrs.doc
468
                    #container_attributes_tokens // From ps.container.attrs.facet
469
                    .build()
470
            };
471
        }
472
    };
473

474
    result
191✔
475
}
191✔
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

© 2026 Coveralls, Inc