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

facet-rs / facet / 19897703279

03 Dec 2025 02:39PM UTC coverage: 58.881% (-0.02%) from 58.902%
19897703279

Pull #1005

github

web-flow
Merge bb581b21b into 40ab0de16
Pull Request #1005: docs: comprehensive documentation update for release readiness

0 of 13 new or added lines in 1 file covered. (0.0%)

220 existing lines in 6 files now uncovered.

20587 of 34964 relevant lines covered (58.88%)

539.09 hits per line

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

88.58
/facet-macros-impl/src/process_enum.rs
1
use super::*;
2
use crate::{
3
    parsed::{IdentOrLiteral, PRepr, PVariantKind, PrimitiveRepr},
4
    process_struct::gen_field_from_pfield,
5
};
6
use quote::{format_ident, quote};
7

8
/// Processes an enum to implement Facet
9
pub(crate) fn process_enum(parsed: Enum) -> TokenStream {
265✔
10
    // Use already-parsed PEnum, including container/variant/field attributes and rename rules
11
    let pe = PEnum::parse(&parsed);
265✔
12

13
    let enum_name = &pe.container.name;
265✔
14
    let enum_name_str = enum_name.to_string();
265✔
15

16
    let opaque = pe
265✔
17
        .container
265✔
18
        .attrs
265✔
19
        .facet
265✔
20
        .iter()
265✔
21
        .any(|a| a.is_builtin() && a.key_str() == "opaque");
265✔
22

23
    // Get the facet crate path (custom or default ::facet)
24
    let facet_crate = pe.container.attrs.facet_crate();
265✔
25

26
    let type_name_fn =
265✔
27
        generate_type_name_fn(enum_name, parsed.generics.as_ref(), opaque, &facet_crate);
265✔
28

29
    let bgp = pe.container.bgp.clone();
265✔
30
    // Use the AST directly for where clauses and generics, as PContainer/PEnum doesn't store them
31
    let where_clauses_tokens = build_where_clauses(
265✔
32
        parsed.clauses.as_ref(),
265✔
33
        parsed.generics.as_ref(),
265✔
34
        opaque,
265✔
35
        &facet_crate,
265✔
36
    );
37
    let type_params = build_type_params(parsed.generics.as_ref(), opaque, &facet_crate);
265✔
38

39
    // Container-level docs from PAttrs
40
    let maybe_container_doc = match &pe.container.attrs.doc[..] {
265✔
41
        [] => quote! {},
265✔
42
        doc_lines => quote! { .doc(&[#(#doc_lines),*]) },
9✔
43
    };
44

45
    let container_attributes_tokens = {
265✔
46
        let mut attribute_tokens: Vec<TokenStream> = Vec::new();
265✔
47
        for attr in &pe.container.attrs.facet {
265✔
48
            // Skip crate attribute - it's handled specially
49
            if attr.is_builtin() && attr.key_str() == "crate" {
37✔
50
                continue;
1✔
51
            }
36✔
52
            // All attributes go through grammar dispatch
53
            let ext_attr = emit_attr(attr, &facet_crate);
36✔
54
            attribute_tokens.push(quote! { #ext_attr });
36✔
55
        }
56

57
        if attribute_tokens.is_empty() {
265✔
58
            quote! {}
237✔
59
        } else {
60
            quote! { .attributes(&const { [#(#attribute_tokens),*] }) }
28✔
61
        }
62
    };
63

64
    let type_tag_maybe = {
265✔
65
        if let Some(type_tag) = pe.container.attrs.get_builtin_args("type_tag") {
265✔
UNCOV
66
            quote! { .type_tag(#type_tag) }
×
67
        } else {
68
            quote! {}
265✔
69
        }
70
    };
71

72
    // Determine enum repr (already resolved by PEnum::parse())
73
    let valid_repr = &pe.repr;
265✔
74

75
    // Are these relevant for enums? Or is it always `repr(C)` if a `PrimitiveRepr` is present?
76
    let repr = match &valid_repr {
265✔
UNCOV
77
        PRepr::Transparent => unreachable!("this should be caught by PRepr::parse"),
×
78
        PRepr::Rust(_) => quote! { #facet_crate::Repr::default() },
228✔
79
        PRepr::C(_) => quote! { #facet_crate::Repr::c() },
37✔
80
    };
81

82
    // Helper for EnumRepr TS (token stream) generation for primitives
83
    let enum_repr_ts_from_primitive = |primitive_repr: PrimitiveRepr| -> TokenStream {
265✔
84
        let type_name_str = primitive_repr.type_name().to_string();
230✔
85
        let enum_repr_variant_ident = format_ident!("{}", type_name_str.to_uppercase());
230✔
86
        quote! { #facet_crate::EnumRepr::#enum_repr_variant_ident }
230✔
87
    };
230✔
88

89
    // --- Processing code for shadow struct/fields/variant_expressions ---
90
    // A. C-style enums have shadow-discriminant, shadow-union, shadow-struct
91
    // B. Primitive enums have simpler layout.
92
    let (shadow_struct_defs, variant_expressions, enum_repr_type_tokenstream) = match valid_repr {
265✔
93
        PRepr::C(prim_opt) => {
37✔
94
            // Shadow discriminant
95
            let shadow_discriminant_name =
37✔
96
                quote::format_ident!("__Shadow_CRepr_Discriminant_for_{}", enum_name_str);
37✔
97
            let all_variant_names: Vec<Ident> = pe
37✔
98
                .variants
37✔
99
                .iter()
37✔
100
                .map(|pv| match &pv.name.raw {
90✔
101
                    IdentOrLiteral::Ident(id) => id.clone(),
90✔
UNCOV
102
                    IdentOrLiteral::Literal(n) => format_ident!("_{}", n), // Should not happen for enums
×
103
                })
90✔
104
                .collect();
37✔
105

106
            let repr_attr_content = match prim_opt {
37✔
107
                Some(p) => p.type_name(),
2✔
108
                None => quote! { C },
35✔
109
            };
110
            let mut shadow_defs = vec![quote! {
37✔
111
                #[repr(#repr_attr_content)]
112
                #[allow(dead_code)]
113
                enum #shadow_discriminant_name { #(#all_variant_names),* }
114
            }];
115

116
            // Shadow union
117
            let shadow_union_name =
37✔
118
                quote::format_ident!("__Shadow_CRepr_Fields_Union_for_{}", enum_name_str);
37✔
119
            let facet_bgp = bgp.with_lifetime(LifetimeName(format_ident!("__facet")));
37✔
120
            let bgp_with_bounds = facet_bgp.display_with_bounds();
37✔
121
            let bgp_without_bounds = facet_bgp.display_without_bounds();
37✔
122
            let phantom_data = facet_bgp.display_as_phantom_data();
37✔
123
            let all_union_fields: Vec<TokenStream> = pe.variants.iter().map(|pv| {
90✔
124
                // Each field is named after the variant, struct for its fields.
125
                let variant_ident = match &pv.name.raw {
90✔
126
                    IdentOrLiteral::Ident(id) => id.clone(),
90✔
UNCOV
127
                     IdentOrLiteral::Literal(idx) => format_ident!("_{}", idx), // Should not happen
×
128
                };
129
                let shadow_field_name_ident = quote::format_ident!("__Shadow_CRepr_Field{}_{}", enum_name_str, variant_ident);
90✔
130
                quote! {
90✔
131
                    #variant_ident: ::core::mem::ManuallyDrop<#shadow_field_name_ident #bgp_without_bounds>
132
                }
133
            }).collect();
90✔
134

135
            shadow_defs.push(quote! {
37✔
136
                #[repr(C)]
137
                #[allow(non_snake_case, dead_code)]
138
                union #shadow_union_name #bgp_with_bounds #where_clauses_tokens { #(#all_union_fields),* }
139
            });
140

141
            // Shadow repr struct for enum as a whole
142
            let shadow_repr_name =
37✔
143
                quote::format_ident!("__Shadow_CRepr_Struct_for_{}", enum_name_str);
37✔
144
            shadow_defs.push(quote! {
37✔
145
                #[repr(C)]
146
                #[allow(non_snake_case)]
147
                #[allow(dead_code)]
148
                struct #shadow_repr_name #bgp_with_bounds #where_clauses_tokens {
149
                    _discriminant: #shadow_discriminant_name,
150
                    _phantom: #phantom_data,
151
                    _fields: #shadow_union_name #bgp_without_bounds,
152
                }
153
            });
154

155
            // Generate variant_expressions
156
            let mut discriminant: Option<&TokenStream> = None;
37✔
157
            let mut discriminant_offset: i64 = 0;
37✔
158
            let mut exprs = Vec::new();
37✔
159

160
            for pv in pe.variants.iter() {
90✔
161
                if let Some(dis) = &pv.discriminant {
90✔
UNCOV
162
                    discriminant = Some(dis);
×
UNCOV
163
                    discriminant_offset = 0;
×
164
                }
90✔
165

166
                let discriminant_ts = if let Some(discriminant) = discriminant {
90✔
UNCOV
167
                    if discriminant_offset > 0 {
×
UNCOV
168
                        quote! { #discriminant + #discriminant_offset }
×
169
                    } else {
UNCOV
170
                        quote! { #discriminant }
×
171
                    }
172
                } else {
173
                    quote! { #discriminant_offset }
90✔
174
                };
175

176
                let display_name = pv.name.effective.clone();
90✔
177
                let variant_attrs_tokens = {
90✔
178
                    let name_token = TokenTree::Literal(Literal::string(&display_name));
90✔
179
                    // All attributes go through grammar dispatch
180
                    if pv.attrs.facet.is_empty() {
90✔
181
                        quote! { .name(#name_token) }
90✔
182
                    } else {
UNCOV
183
                        let attrs_list: Vec<TokenStream> = pv
×
184
                            .attrs
×
UNCOV
185
                            .facet
×
UNCOV
186
                            .iter()
×
UNCOV
187
                            .map(|attr| {
×
UNCOV
188
                                let ext_attr = emit_attr(attr, &facet_crate);
×
UNCOV
189
                                quote! { #ext_attr }
×
UNCOV
190
                            })
×
UNCOV
191
                            .collect();
×
UNCOV
192
                        quote! { .name(#name_token).attributes(&const { [#(#attrs_list),*] }) }
×
193
                    }
194
                };
195

196
                let maybe_doc = match &pv.attrs.doc[..] {
90✔
197
                    [] => quote! {},
90✔
198
                    doc_lines => quote! { .doc(&[#(#doc_lines),*]) },
×
199
                };
200

201
                let shadow_struct_name = match &pv.name.raw {
90✔
202
                    IdentOrLiteral::Ident(id) => {
90✔
203
                        // Use the same naming convention as in the union definition
204
                        quote::format_ident!("__Shadow_CRepr_Field{}_{}", enum_name_str, id)
90✔
205
                    }
206
                    IdentOrLiteral::Literal(idx) => {
×
207
                        // Use the same naming convention as in the union definition
UNCOV
208
                        quote::format_ident!(
×
209
                            "__Shadow_CRepr_Field{}_{}",
210
                            enum_name_str,
UNCOV
211
                            format_ident!("_{}", idx) // Should not happen
×
212
                        )
213
                    }
214
                };
215

216
                let variant_offset = quote! {
90✔
217
                    ::core::mem::offset_of!(#shadow_repr_name #bgp_without_bounds, _fields)
218
                };
219

220
                // Determine field structure for the variant
221
                match &pv.kind {
90✔
222
                    PVariantKind::Unit => {
14✔
223
                        // Generate unit shadow struct for the variant
14✔
224
                        shadow_defs.push(quote! {
14✔
225
                            #[repr(C)]
14✔
226
                            #[allow(non_snake_case, dead_code)]
14✔
227
                            struct #shadow_struct_name #bgp_with_bounds #where_clauses_tokens { _phantom: #phantom_data }
14✔
228
                        });
14✔
229
                        exprs.push(quote! {
14✔
230
                            #facet_crate::Variant::builder()
14✔
231
                                #variant_attrs_tokens
14✔
232
                                .discriminant(#discriminant_ts as i64)
14✔
233
                                .data(#facet_crate::StructType::builder().repr(#facet_crate::Repr::c()).unit().build())
14✔
234
                                #maybe_doc
14✔
235
                                .build()
14✔
236
                        });
14✔
237
                    }
14✔
238
                    PVariantKind::Tuple { fields } => {
38✔
239
                        // Tuple shadow struct
240
                        let fields_with_types: Vec<TokenStream> = fields
38✔
241
                            .iter()
38✔
242
                            .enumerate()
38✔
243
                            .map(|(idx, pf)| {
50✔
244
                                let field_ident = format_ident!("_{}", idx);
50✔
245
                                let typ = &pf.ty;
50✔
246
                                quote! { #field_ident: #typ }
50✔
247
                            })
50✔
248
                            .collect();
38✔
249
                        shadow_defs.push(quote! {
38✔
250
                            #[repr(C)]
251
                            #[allow(non_snake_case, dead_code)]
252
                            struct #shadow_struct_name #bgp_with_bounds #where_clauses_tokens {
253
                                #(#fields_with_types),* ,
254
                                _phantom: #phantom_data
255
                            }
256
                        });
257
                        let field_defs: Vec<TokenStream> = fields
38✔
258
                            .iter()
38✔
259
                            .enumerate()
38✔
260
                            .map(|(idx, pf)| {
50✔
261
                                let mut pfield = pf.clone();
50✔
262
                                let field_ident = format_ident!("_{}", idx);
50✔
263
                                pfield.name.raw = IdentOrLiteral::Ident(field_ident);
50✔
264
                                gen_field_from_pfield(
50✔
265
                                    &pfield,
50✔
266
                                    &shadow_struct_name,
50✔
267
                                    &facet_bgp,
50✔
268
                                    Some(variant_offset.clone()),
50✔
269
                                    &facet_crate,
50✔
270
                                )
271
                            })
50✔
272
                            .collect();
38✔
273
                        exprs.push(quote! {{
38✔
274
                            let fields: &'static [#facet_crate::Field] = &const {[
275
                                #(#field_defs),*
276
                            ]};
277
                            #facet_crate::Variant::builder()
278
                                #variant_attrs_tokens
279
                                .discriminant(#discriminant_ts as i64)
280
                                .data(#facet_crate::StructType::builder().repr(#facet_crate::Repr::c()).tuple().fields(fields).build())
281
                                #maybe_doc
282
                                .build()
283
                        }});
284
                    }
285
                    PVariantKind::Struct { fields } => {
38✔
286
                        let fields_with_types: Vec<TokenStream> = fields
38✔
287
                            .iter()
38✔
288
                            .map(|pf| {
65✔
289
                                // Use raw name for struct field definition
290
                                let field_name = match &pf.name.raw {
65✔
291
                                    IdentOrLiteral::Ident(id) => quote! { #id },
65✔
292
                                    IdentOrLiteral::Literal(_) => {
UNCOV
293
                                        panic!("Struct variant cannot have literal field names")
×
294
                                    }
295
                                };
296
                                let typ = &pf.ty;
65✔
297
                                quote! { #field_name: #typ }
65✔
298
                            })
65✔
299
                            .collect();
38✔
300

301
                        // Handle empty fields case explicitly
302
                        let struct_fields = if fields_with_types.is_empty() {
38✔
303
                            // Only add phantom data for empty struct variants
UNCOV
304
                            quote! { _phantom: #phantom_data }
×
305
                        } else {
306
                            // Add fields plus phantom data for non-empty struct variants
307
                            quote! { #(#fields_with_types),*, _phantom: #phantom_data }
38✔
308
                        };
309
                        shadow_defs.push(quote! {
38✔
310
                            #[repr(C)]
311
                            #[allow(non_snake_case, dead_code)]
312
                            struct #shadow_struct_name #bgp_with_bounds #where_clauses_tokens {
313
                                #struct_fields
314
                            }
315
                        });
316

317
                        let field_defs: Vec<TokenStream> = fields
38✔
318
                            .iter()
38✔
319
                            .map(|pf| {
65✔
320
                                gen_field_from_pfield(
65✔
321
                                    pf,
65✔
322
                                    &shadow_struct_name,
65✔
323
                                    &facet_bgp,
65✔
324
                                    Some(variant_offset.clone()),
65✔
325
                                    &facet_crate,
65✔
326
                                )
327
                            })
65✔
328
                            .collect();
38✔
329

330
                        exprs.push(quote! {{
38✔
331
                            let fields: &'static [#facet_crate::Field] = &const {[
332
                                #(#field_defs),*
333
                            ]};
334
                            #facet_crate::Variant::builder()
335
                                #variant_attrs_tokens
336
                                .discriminant(#discriminant_ts as i64)
337
                                .data(#facet_crate::StructType::builder().repr(#facet_crate::Repr::c()).struct_().fields(fields).build())
338
                                #maybe_doc
339
                                .build()
340
                        }});
341
                    }
342
                };
343

344
                // C-style enums increment discriminant unless explicitly set
345
                discriminant_offset += 1;
90✔
346
            }
347

348
            // Generate the EnumRepr token stream
349
            let repr_type_ts = match prim_opt {
37✔
350
                None => {
351
                    quote! { #facet_crate::EnumRepr::from_discriminant_size::<#shadow_discriminant_name>() }
35✔
352
                }
353
                Some(p) => enum_repr_ts_from_primitive(*p),
2✔
354
            };
355

356
            (shadow_defs, exprs, repr_type_ts)
37✔
357
        }
358
        PRepr::Rust(Some(prim)) => {
228✔
359
            // Treat as primitive repr
360
            let facet_bgp = bgp.with_lifetime(LifetimeName(format_ident!("__facet")));
228✔
361
            let bgp_with_bounds = facet_bgp.display_with_bounds();
228✔
362
            let phantom_data = facet_bgp.display_as_phantom_data();
228✔
363
            let discriminant_rust_type = prim.type_name();
228✔
364
            let mut shadow_defs = Vec::new();
228✔
365

366
            // Generate variant_expressions
367
            let mut discriminant: Option<&TokenStream> = None;
228✔
368
            let mut discriminant_offset: i64 = 0;
228✔
369

370
            let mut exprs = Vec::new();
228✔
371

372
            for pv in pe.variants.iter() {
535✔
373
                if let Some(dis) = &pv.discriminant {
535✔
374
                    discriminant = Some(dis);
26✔
375
                    discriminant_offset = 0;
26✔
376
                }
509✔
377

378
                let discriminant_ts = if let Some(discriminant) = discriminant {
535✔
379
                    if discriminant_offset > 0 {
26✔
UNCOV
380
                        quote! { #discriminant + #discriminant_offset }
×
381
                    } else {
382
                        quote! { #discriminant }
26✔
383
                    }
384
                } else {
385
                    quote! { #discriminant_offset }
509✔
386
                };
387

388
                let display_name = pv.name.effective.clone();
535✔
389
                let variant_attrs_tokens = {
535✔
390
                    let name_token = TokenTree::Literal(Literal::string(&display_name));
535✔
391
                    // All attributes go through grammar dispatch
392
                    if pv.attrs.facet.is_empty() {
535✔
393
                        quote! { .name(#name_token) }
523✔
394
                    } else {
395
                        let attrs_list: Vec<TokenStream> = pv
12✔
396
                            .attrs
12✔
397
                            .facet
12✔
398
                            .iter()
12✔
399
                            .map(|attr| {
12✔
400
                                let ext_attr = emit_attr(attr, &facet_crate);
12✔
401
                                quote! { #ext_attr }
12✔
402
                            })
12✔
403
                            .collect();
12✔
404
                        quote! { .name(#name_token).attributes(&const { [#(#attrs_list),*] }) }
12✔
405
                    }
406
                };
407

408
                let maybe_doc = match &pv.attrs.doc[..] {
535✔
409
                    [] => quote! {},
535✔
410
                    doc_lines => quote! { .doc(&[#(#doc_lines),*]) },
27✔
411
                };
412

413
                match &pv.kind {
535✔
414
                    PVariantKind::Unit => {
174✔
415
                        exprs.push(quote! {
174✔
416
                            #facet_crate::Variant::builder()
174✔
417
                                #variant_attrs_tokens
174✔
418
                                .discriminant(#discriminant_ts as i64)
174✔
419
                                .data(#facet_crate::StructType::builder().repr(#facet_crate::Repr::c()).unit().build())
174✔
420
                                #maybe_doc
174✔
421
                                .build()
174✔
422
                        });
174✔
423
                    }
174✔
424
                    PVariantKind::Tuple { fields } => {
222✔
425
                        let shadow_struct_name = match &pv.name.raw {
222✔
426
                            IdentOrLiteral::Ident(id) => {
222✔
427
                                quote::format_ident!(
222✔
428
                                    "__Shadow_RustRepr_Tuple_for_{}_{}",
429
                                    enum_name_str,
430
                                    id
431
                                )
432
                            }
433
                            IdentOrLiteral::Literal(_) => {
UNCOV
434
                                panic!(
×
435
                                    "Enum variant names cannot be literals for tuple variants in #[repr(Rust)]"
436
                                )
437
                            }
438
                        };
439
                        let fields_with_types: Vec<TokenStream> = fields
222✔
440
                            .iter()
222✔
441
                            .enumerate()
222✔
442
                            .map(|(idx, pf)| {
246✔
443
                                let field_ident = format_ident!("_{}", idx);
246✔
444
                                let typ = &pf.ty;
246✔
445
                                quote! { #field_ident: #typ }
246✔
446
                            })
246✔
447
                            .collect();
222✔
448
                        shadow_defs.push(quote! {
222✔
449
                            #[repr(C)] // Layout variants like C structs
450
                            #[allow(non_snake_case, dead_code)]
451
                            struct #shadow_struct_name #bgp_with_bounds #where_clauses_tokens {
452
                                _discriminant: #discriminant_rust_type,
453
                                _phantom: #phantom_data,
454
                                #(#fields_with_types),*
455
                            }
456
                        });
457
                        let field_defs: Vec<TokenStream> = fields
222✔
458
                            .iter()
222✔
459
                            .enumerate()
222✔
460
                            .map(|(idx, pf)| {
246✔
461
                                let mut pf = pf.clone();
246✔
462
                                let field_ident = format_ident!("_{}", idx);
246✔
463
                                pf.name.raw = IdentOrLiteral::Ident(field_ident);
246✔
464
                                gen_field_from_pfield(
246✔
465
                                    &pf,
246✔
466
                                    &shadow_struct_name,
246✔
467
                                    &facet_bgp,
246✔
468
                                    None,
246✔
469
                                    &facet_crate,
246✔
470
                                )
471
                            })
246✔
472
                            .collect();
222✔
473
                        exprs.push(quote! {{
222✔
474
                            let fields: &'static [#facet_crate::Field] = &const {[
475
                                #(#field_defs),*
476
                            ]};
477
                            #facet_crate::Variant::builder()
478
                                #variant_attrs_tokens
479
                                .discriminant(#discriminant_ts as i64)
480
                                .data(#facet_crate::StructType::builder().repr(#facet_crate::Repr::c()).tuple().fields(fields).build())
481
                                #maybe_doc
482
                                .build()
483
                        }});
484
                    }
485
                    PVariantKind::Struct { fields } => {
139✔
486
                        let shadow_struct_name = match &pv.name.raw {
139✔
487
                            IdentOrLiteral::Ident(id) => {
139✔
488
                                // Use a more descriptive name, similar to the Tuple variant case
489
                                quote::format_ident!(
139✔
490
                                    "__Shadow_RustRepr_Struct_for_{}_{}",
491
                                    enum_name_str,
492
                                    id
493
                                )
494
                            }
495
                            IdentOrLiteral::Literal(_) => {
496
                                // This case should ideally not happen for named struct variants
UNCOV
497
                                panic!(
×
498
                                    "Enum variant names cannot be literals for struct variants in #[repr(Rust)]"
499
                                )
500
                            }
501
                        };
502
                        let fields_with_types: Vec<TokenStream> = fields
139✔
503
                            .iter()
139✔
504
                            .map(|pf| {
205✔
505
                                let field_name = match &pf.name.raw {
205✔
506
                                    IdentOrLiteral::Ident(id) => quote! { #id },
205✔
507
                                    IdentOrLiteral::Literal(_) => {
UNCOV
508
                                        panic!("Struct variant cannot have literal field names")
×
509
                                    }
510
                                };
511
                                let typ = &pf.ty;
205✔
512
                                quote! { #field_name: #typ }
205✔
513
                            })
205✔
514
                            .collect();
139✔
515
                        shadow_defs.push(quote! {
139✔
516
                            #[repr(C)] // Layout variants like C structs
517
                            #[allow(non_snake_case, dead_code)]
518
                            struct #shadow_struct_name #bgp_with_bounds #where_clauses_tokens {
519
                                _discriminant: #discriminant_rust_type,
520
                                _phantom: #phantom_data,
521
                                #(#fields_with_types),*
522
                            }
523
                        });
524
                        let field_defs: Vec<TokenStream> = fields
139✔
525
                            .iter()
139✔
526
                            .map(|pf| {
205✔
527
                                gen_field_from_pfield(
205✔
528
                                    pf,
205✔
529
                                    &shadow_struct_name,
205✔
530
                                    &facet_bgp,
205✔
531
                                    None,
205✔
532
                                    &facet_crate,
205✔
533
                                )
534
                            })
205✔
535
                            .collect();
139✔
536
                        exprs.push(quote! {{
139✔
537
                            let fields: &'static [#facet_crate::Field] = &const {[
538
                                #(#field_defs),*
539
                            ]};
540
                            #facet_crate::Variant::builder()
541
                                #variant_attrs_tokens
542
                                .discriminant(#discriminant_ts as i64)
543
                                .data(#facet_crate::StructType::builder().repr(#facet_crate::Repr::c()).struct_().fields(fields).build())
544
                                #maybe_doc
545
                                .build()
546
                        }});
547
                    }
548
                }
549
                // Rust-style enums increment discriminant unless explicitly set
550
                discriminant_offset += 1;
535✔
551
            }
552
            let repr_type_ts = enum_repr_ts_from_primitive(*prim);
228✔
553
            (shadow_defs, exprs, repr_type_ts)
228✔
554
        }
555
        PRepr::Transparent => {
UNCOV
556
            return quote! {
×
557
                compile_error!("#[repr(transparent)] is not supported on enums by Facet");
558
            };
559
        }
560
        PRepr::Rust(None) => {
UNCOV
561
            return quote! {
×
562
                compile_error!("Facet requires enums to have an explicit representation (e.g., #[repr(C)], #[repr(u8)])");
563
            };
564
        }
565
    };
566

567
    // Only make static_decl for non-generic enums
568
    let static_decl = if parsed.generics.is_none() {
265✔
569
        generate_static_decl(enum_name, &facet_crate)
255✔
570
    } else {
571
        quote! {}
10✔
572
    };
573

574
    // Set up generics for impl blocks
575
    let facet_bgp = bgp.with_lifetime(LifetimeName(format_ident!("__facet")));
265✔
576
    let bgp_def = facet_bgp.display_with_bounds();
265✔
577
    let bgp_without_bounds = bgp.display_without_bounds();
265✔
578

579
    let (ty, fields) = if opaque {
265✔
580
        (
×
581
            quote! {
×
582
                .ty(#facet_crate::Type::User(#facet_crate::UserType::Opaque))
×
583
            },
×
UNCOV
584
            quote! {},
×
UNCOV
585
        )
×
586
    } else {
587
        (
588
            quote! {
265✔
589
                .ty(#facet_crate::Type::User(#facet_crate::UserType::Enum(#facet_crate::EnumType::builder()
590
                        // Use variant expressions that just reference the shadow structs
591
                        // which are now defined above
592
                        .variants(__facet_variants)
593
                        .repr(#repr)
594
                        .enum_repr(#enum_repr_type_tokenstream)
595
                        .build())
596
                ))
597
            },
598
            quote! {
265✔
599
                let __facet_variants: &'static [#facet_crate::Variant] = &const {[
600
                    #(#variant_expressions),*
601
                ]};
602
            },
603
        )
604
    };
605

606
    // Generate the impl
607
    quote! {
265✔
608
        #static_decl
609

610
        #[automatically_derived]
611
        #[allow(non_camel_case_types)]
612
        unsafe impl #bgp_def #facet_crate::Facet<'__facet> for #enum_name #bgp_without_bounds #where_clauses_tokens {
613
            const SHAPE: &'static #facet_crate::Shape = &const {
614
                #(#shadow_struct_defs)*
615
                #fields
616
                #facet_crate::Shape::builder_for_sized::<Self>()
617
                    .vtable({
618
                        #facet_crate::value_vtable!(Self, #type_name_fn)
619
                    })
620
                    .type_identifier(#enum_name_str)
621
                    #type_params
622
                    #ty
623
                    #maybe_container_doc
624
                    #container_attributes_tokens
625
                    #type_tag_maybe
626
                    .build()
627
            };
628
        }
629
    }
630
}
265✔
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