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

facet-rs / facet / 15066189877

16 May 2025 10:17AM UTC coverage: 56.279% (-1.8%) from 58.036%
15066189877

push

github

fasterthanlime
More Rust 1.87 fixess

8954 of 15910 relevant lines covered (56.28%)

126.71 hits per line

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

87.62
/facet-derive-emit/src/process_enum.rs
1
use super::*;
2
// Import PRepr, PrimitiveRepr, PStructField, etc. from parsed module
3
use crate::{
4
    parsed::{IdentOrLiteral, PFacetAttr, PRepr, PVariantKind, PrimitiveRepr},
5
    process_struct::gen_field_from_pfield,
6
};
7
use quote::{format_ident, quote};
8

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

14
    let enum_name = pe.container.name.clone();
99✔
15
    let enum_name_str = enum_name.to_string();
99✔
16
    let bgp = pe.container.bgp.clone();
99✔
17
    // Use the AST directly for where clauses and generics, as PContainer/PEnum doesn't store them
18
    let where_clauses_tokens =
99✔
19
        build_where_clauses(parsed.clauses.as_ref(), parsed.generics.as_ref());
99✔
20
    let type_params = build_type_params(parsed.generics.as_ref());
99✔
21

22
    // Container-level docs from PAttrs
23
    let maybe_container_doc = match &pe.container.attrs.doc[..] {
99✔
24
        [] => quote! {},
99✔
25
        doc_lines => quote! { .doc(&[#(#doc_lines),*]) },
5✔
26
    };
27

28
    let container_attributes_tokens = {
99✔
29
        let mut attribute_tokens: Vec<TokenStream> = Vec::new();
99✔
30
        for attr in &pe.container.attrs.facet {
104✔
31
            match attr {
5✔
32
                PFacetAttr::DenyUnknownFields => {
×
33
                    attribute_tokens.push(quote! { ::facet::ShapeAttribute::DenyUnknownFields });
×
34
                }
×
35
                PFacetAttr::Arbitrary { content } => {
2✔
36
                    attribute_tokens.push(quote! { ::facet::ShapeAttribute::Arbitrary(#content) });
2✔
37
                }
2✔
38
                PFacetAttr::RenameAll { rule } => {
3✔
39
                    // RenameAll is handled by PName logic, but add it as ShapeAttribute too
3✔
40
                    let rule_str = rule.apply(""); // Hack to get str - improve RenameRule display
3✔
41
                    attribute_tokens.push(quote! { ::facet::ShapeAttribute::RenameAll(#rule_str) });
3✔
42
                }
3✔
43
                PFacetAttr::Invariants { .. } => {
44
                    // Note: Facet vtable does not currently support invariants directly on enums
45
                    // Maybe panic or warn here? For now, ignoring.
46
                    panic!("Invariants are not supported on enums")
×
47
                }
48
                // Opaque, Transparent, SkipSerializing/If, Default/Equals are not relevant/valid for enum containers.
49
                _ => {}
×
50
            }
51
        }
52

53
        if attribute_tokens.is_empty() {
99✔
54
            quote! {}
95✔
55
        } else {
56
            quote! { .attributes(&const { [#(#attribute_tokens),*] }) }
4✔
57
        }
58
    };
59

60
    // Determine enum repr (already resolved by PEnum::parse())
61
    let valid_repr = &pe.repr;
99✔
62

63
    // Helper for EnumRepr TS (token stream) generation for primitives
64
    fn enum_repr_ts_from_primitive(primitive_repr: PrimitiveRepr) -> TokenStream {
79✔
65
        let type_name_str = primitive_repr.type_name().to_string();
79✔
66
        let enum_repr_variant_ident = format_ident!("{}", type_name_str.to_uppercase());
79✔
67
        quote! { ::facet::EnumRepr::#enum_repr_variant_ident }
79✔
68
    }
79✔
69

70
    // --- Processing code for shadow struct/fields/variant_expressions ---
71
    // A. C-style enums have shadow-discriminant, shadow-union, shadow-struct
72
    // B. Primitive enums have simpler layout.
73
    let (shadow_struct_defs, variant_expressions, enum_repr_type_tokenstream) = match valid_repr {
99✔
74
        PRepr::C(prim_opt) => {
22✔
75
            // Shadow discriminant
76
            let shadow_discriminant_name =
22✔
77
                quote::format_ident!("__Shadow_CRepr_Discriminant_for_{}", enum_name_str);
22✔
78
            let all_variant_names: Vec<Ident> = pe
22✔
79
                .variants
22✔
80
                .iter()
22✔
81
                .map(|pv| match &pv.name.raw {
53✔
82
                    IdentOrLiteral::Ident(id) => id.clone(),
53✔
83
                    IdentOrLiteral::Literal(n) => format_ident!("_{}", n), // Should not happen for enums
×
84
                })
53✔
85
                .collect();
22✔
86

87
            let repr_attr_content = match prim_opt {
22✔
88
                Some(p) => p.type_name(),
2✔
89
                None => quote! { C },
20✔
90
            };
91
            let mut shadow_defs = vec![quote! {
22✔
92
                #[repr(#repr_attr_content)]
93
                #[allow(dead_code)]
94
                enum #shadow_discriminant_name { #(#all_variant_names),* }
95
            }];
96

97
            // Shadow union
98
            let shadow_union_name =
22✔
99
                quote::format_ident!("__Shadow_CRepr_Fields_Union_for_{}", enum_name_str);
22✔
100
            let facet_bgp = bgp.with_lifetime(LifetimeName(format_ident!("__facet")));
22✔
101
            let bgp_with_bounds = facet_bgp.display_with_bounds();
22✔
102
            let bgp_without_bounds = facet_bgp.display_without_bounds();
22✔
103
            let phantom_data = facet_bgp.display_as_phantom_data();
22✔
104
            let all_union_fields: Vec<TokenStream> = pe.variants.iter().map(|pv| {
53✔
105
                // Each field is named after the variant, struct for its fields.
106
                let variant_ident = match &pv.name.raw {
53✔
107
                    IdentOrLiteral::Ident(id) => id.clone(),
53✔
108
                     IdentOrLiteral::Literal(idx) => format_ident!("_{}", idx), // Should not happen
×
109
                };
110
                let shadow_field_name_ident = quote::format_ident!("__Shadow_CRepr_Field{}_{}", enum_name_str, variant_ident);
53✔
111
                quote! {
53✔
112
                    #variant_ident: ::core::mem::ManuallyDrop<#shadow_field_name_ident #bgp_without_bounds>
113
                }
114
            }).collect();
53✔
115

116
            shadow_defs.push(quote! {
22✔
117
                #[repr(C)]
118
                #[allow(non_snake_case, dead_code)]
119
                union #shadow_union_name #bgp_with_bounds #where_clauses_tokens { #(#all_union_fields),* }
120
            });
121

122
            // Shadow repr struct for enum as a whole
123
            let shadow_repr_name =
22✔
124
                quote::format_ident!("__Shadow_CRepr_Struct_for_{}", enum_name_str);
22✔
125
            shadow_defs.push(quote! {
22✔
126
                #[repr(C)]
127
                #[allow(non_snake_case)]
128
                #[allow(dead_code)]
129
                struct #shadow_repr_name #bgp_with_bounds #where_clauses_tokens {
130
                    _discriminant: #shadow_discriminant_name,
131
                    _phantom: #phantom_data,
132
                    _fields: #shadow_union_name #bgp_without_bounds,
133
                }
134
            });
135

136
            // Generate variant_expressions
137
            let mut discriminant: Option<&TokenStream> = None;
22✔
138
            let mut discriminant_offset: i64 = 0;
22✔
139
            let mut exprs = Vec::new();
22✔
140

141
            for pv in pe.variants.iter() {
53✔
142
                if let Some(dis) = &pv.discriminant {
53✔
143
                    discriminant = Some(dis);
×
144
                    discriminant_offset = 0;
×
145
                }
53✔
146

147
                let discriminant_ts = if let Some(discriminant) = discriminant {
53✔
148
                    if discriminant_offset > 0 {
×
149
                        quote! { #discriminant + #discriminant_offset }
×
150
                    } else {
151
                        quote! { #discriminant }
×
152
                    }
153
                } else {
154
                    quote! { #discriminant_offset }
53✔
155
                };
156

157
                let display_name = pv.name.effective.clone();
53✔
158
                let variant_attrs_tokens = {
53✔
159
                    let mut tokens = Vec::new();
53✔
160
                    let name_token = TokenTree::Literal(Literal::string(&display_name));
53✔
161
                    // Attributes from PAttrs
162
                    if pv.attrs.facet.is_empty() {
53✔
163
                        tokens.push(quote! { .name(#name_token) });
53✔
164
                    } else {
53✔
165
                        let mut attrs_list = Vec::new();
×
166
                        for attr in &pv.attrs.facet {
×
167
                            if let PFacetAttr::Arbitrary { content } = attr {
×
168
                                attrs_list.push(
×
169
                                    quote! { ::facet::VariantAttribute::Arbitrary(#content) },
×
170
                                );
×
171
                            }
×
172
                        }
173
                        if attrs_list.is_empty() {
×
174
                            tokens.push(quote! { .name(#name_token) });
×
175
                        } else {
×
176
                            tokens.push(
×
177
                                quote! { .name(#name_token).attributes(&[#(#attrs_list),*]) },
×
178
                            );
179
                        }
180
                    }
181
                    quote! { #(#tokens)* }
53✔
182
                };
183

184
                let maybe_doc = match &pv.attrs.doc[..] {
53✔
185
                    [] => quote! {},
53✔
186
                    doc_lines => quote! { .doc(&[#(#doc_lines),*]) },
2✔
187
                };
188

189
                let shadow_struct_name = match &pv.name.raw {
53✔
190
                    IdentOrLiteral::Ident(id) => {
53✔
191
                        // Use the same naming convention as in the union definition
192
                        quote::format_ident!("__Shadow_CRepr_Field{}_{}", enum_name_str, id)
53✔
193
                    }
194
                    IdentOrLiteral::Literal(idx) => {
×
195
                        // Use the same naming convention as in the union definition
196
                        quote::format_ident!(
×
197
                            "__Shadow_CRepr_Field{}_{}",
×
198
                            enum_name_str,
199
                            format_ident!("_{}", idx) // Should not happen
×
200
                        )
201
                    }
202
                };
203

204
                let variant_offset = quote! {
53✔
205
                    ::core::mem::offset_of!(#shadow_repr_name #bgp_without_bounds, _fields)
206
                };
207

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

288
                        // Handle empty fields case explicitly
289
                        let struct_fields = if fields_with_types.is_empty() {
21✔
290
                            // Only add phantom data for empty struct variants
291
                            quote! { _phantom: #phantom_data }
1✔
292
                        } else {
293
                            // Add fields plus phantom data for non-empty struct variants
294
                            quote! { #(#fields_with_types),*, _phantom: #phantom_data }
20✔
295
                        };
296
                        shadow_defs.push(quote! {
21✔
297
                            #[repr(C)]
298
                            #[allow(non_snake_case, dead_code)]
299
                            struct #shadow_struct_name #bgp_with_bounds #where_clauses_tokens {
300
                                #struct_fields
301
                            }
302
                        });
303

304
                        let field_defs: Vec<TokenStream> = fields
21✔
305
                            .iter()
21✔
306
                            .map(|pf| {
38✔
307
                                gen_field_from_pfield(
38✔
308
                                    pf,
38✔
309
                                    &shadow_struct_name,
38✔
310
                                    &facet_bgp,
38✔
311
                                    Some(variant_offset.clone()),
38✔
312
                                )
313
                            })
38✔
314
                            .collect();
21✔
315

316
                        exprs.push(quote! {{
21✔
317
                            let fields: &'static [::facet::Field] = &const {[
318
                                #(#field_defs),*
319
                            ]};
320
                            ::facet::Variant::builder()
321
                                #variant_attrs_tokens
322
                                .discriminant(#discriminant_ts)
323
                                .data(::facet::StructType::builder().repr(::facet::Repr::c()).struct_().fields(fields).build())
324
                                #maybe_doc
325
                                .build()
326
                        }});
327
                    }
328
                };
329

330
                // C-style enums increment discriminant unless explicitly set
331
                discriminant_offset += 1;
53✔
332
            }
333

334
            // Generate the EnumRepr token stream
335
            let repr_type_ts = match prim_opt {
22✔
336
                None => {
337
                    quote! { ::facet::EnumRepr::from_discriminant_size::<#shadow_discriminant_name>() }
20✔
338
                }
339
                Some(p) => enum_repr_ts_from_primitive(*p),
2✔
340
            };
341

342
            (shadow_defs, exprs, repr_type_ts)
22✔
343
        }
344
        PRepr::Rust(Some(prim)) => {
77✔
345
            // Treat as primitive repr
346
            let facet_bgp = bgp.with_lifetime(LifetimeName(format_ident!("__facet")));
77✔
347
            let bgp_with_bounds = facet_bgp.display_with_bounds();
77✔
348
            let phantom_data = facet_bgp.display_as_phantom_data();
77✔
349
            let discriminant_rust_type = prim.type_name();
77✔
350
            let mut shadow_defs = Vec::new();
77✔
351

352
            // Generate variant_expressions
353
            let mut discriminant: Option<&TokenStream> = None;
77✔
354
            let mut discriminant_offset: i64 = 0;
77✔
355

356
            let mut exprs = Vec::new();
77✔
357

358
            for pv in pe.variants.iter() {
195✔
359
                if let Some(dis) = &pv.discriminant {
195✔
360
                    discriminant = Some(dis);
21✔
361
                    discriminant_offset = 0;
21✔
362
                }
174✔
363

364
                let discriminant_ts = if let Some(discriminant) = discriminant {
195✔
365
                    if discriminant_offset > 0 {
22✔
366
                        quote! { #discriminant + #discriminant_offset }
1✔
367
                    } else {
368
                        quote! { #discriminant }
21✔
369
                    }
370
                } else {
371
                    quote! { #discriminant_offset }
173✔
372
                };
373

374
                let display_name = pv.name.effective.clone();
195✔
375
                let variant_attrs_tokens = {
195✔
376
                    let mut tokens = Vec::new();
195✔
377
                    let name_token = TokenTree::Literal(Literal::string(&display_name));
195✔
378
                    if pv.attrs.facet.is_empty() {
195✔
379
                        tokens.push(quote! { .name(#name_token) });
192✔
380
                    } else {
192✔
381
                        let mut attrs_list = Vec::new();
3✔
382
                        for attr in &pv.attrs.facet {
7✔
383
                            if let PFacetAttr::Arbitrary { content } = attr {
4✔
384
                                attrs_list.push(
4✔
385
                                    quote! { ::facet::VariantAttribute::Arbitrary(#content) },
4✔
386
                                );
4✔
387
                            }
4✔
388
                        }
389
                        if attrs_list.is_empty() {
3✔
390
                            tokens.push(quote! { .name(#name_token) });
×
391
                        } else {
×
392
                            tokens.push(
3✔
393
                                quote! { .name(#name_token).attributes(&[#(#attrs_list),*]) },
3✔
394
                            );
395
                        }
396
                    }
397
                    quote! { #(#tokens)* }
195✔
398
                };
399

400
                let maybe_doc = match &pv.attrs.doc[..] {
195✔
401
                    [] => quote! {},
195✔
402
                    doc_lines => quote! { .doc(&[#(#doc_lines),*]) },
11✔
403
                };
404

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

547
    // Only make static_decl for non-generic enums
548
    let static_decl = if parsed.generics.is_none() {
99✔
549
        generate_static_decl(&enum_name)
87✔
550
    } else {
551
        quote! {}
12✔
552
    };
553

554
    // Set up generics for impl blocks
555
    let facet_bgp = bgp.with_lifetime(LifetimeName(format_ident!("__facet")));
99✔
556
    let bgp_def = facet_bgp.display_with_bounds();
99✔
557
    let bgp_without_bounds = bgp.display_without_bounds();
99✔
558

559
    // Generate the impl
560
    quote! {
99✔
561
        #static_decl
562

563
        #[automatically_derived]
564
        #[allow(non_camel_case_types)]
565
        unsafe impl #bgp_def ::facet::Facet<'__facet> for #enum_name #bgp_without_bounds #where_clauses_tokens {
566
            const VTABLE: &'static ::facet::ValueVTable = &const { ::facet::value_vtable!(
567
                Self,
568
                |f, _opts| ::core::fmt::Write::write_str(f, #enum_name_str)
569
            )};
570

571
            const SHAPE: &'static ::facet::Shape = &const {
572
                #(#shadow_struct_defs)*
573

574
                let __facet_variants: &'static [::facet::Variant] = &const {[
575
                    #(#variant_expressions),*
576
                ]};
577

578
                ::facet::Shape::builder_for_sized::<Self>()
579
                    #type_params
580
                    .ty(::facet::Type::User(::facet::UserType::Enum(::facet::EnumType::builder()
581
                            // Use variant expressions that just reference the shadow structs
582
                            // which are now defined above
583
                            .variants(__facet_variants)
584
                            .repr(::facet::Repr::c())
585
                            .enum_repr(#enum_repr_type_tokenstream)
586
                            .build())
587
                    ))
588
                    #maybe_container_doc
589
                    #container_attributes_tokens
590
                    .build()
591
            };
592
        }
593
    }
594
}
99✔
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