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

facet-rs / facet / 19826479017

01 Dec 2025 02:41PM UTC coverage: 58.333% (+0.3%) from 58.04%
19826479017

push

github

fasterthanlime
Remove FieldFlags/FieldVTable, make attributes single source of truth

This is a major refactor of how field metadata is stored and accessed:

- Remove FieldFlags bitflags and FieldVTable from Field struct
- All field metadata now lives in the ExtensionAttr attributes array
- Add helper methods: is_flattened(), is_sensitive(), is_child(),
  should_skip_serializing(), default_fn(), skip_serializing_if_fn(), etc.
- Remove deserialize_with/serialize_with syntax entirely
- Use proxy = ProxyType pattern exclusively for custom serialization

Grammar changes:
- Add builtin attribute grammar with variant kinds: unit, newtype,
  newtype_str, predicate, fn_ptr, make_t, shape_type
- Third-party crates now have same power as built-ins via namespaced
  attributes (e.g., #[facet(kdl::child)])

API changes:
- fields_for_serialize() now returns (FieldItem, Peek) instead of
  (Field, Peek) to support runtime field name changes for flattening
- Add KdlFieldExt trait for kdl::child attribute compatibility

561 of 829 new or added lines in 29 files covered. (67.67%)

52 existing lines in 16 files now uncovered.

18961 of 32505 relevant lines covered (58.33%)

185.16 hits per line

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

87.91
/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 {
230✔
10
    // Use already-parsed PEnum, including container/variant/field attributes and rename rules
11
    let pe = PEnum::parse(&parsed);
230✔
12

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

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

23
    let type_name_fn = generate_type_name_fn(enum_name, parsed.generics.as_ref(), opaque);
230✔
24

25
    let bgp = pe.container.bgp.clone();
230✔
26
    // Use the AST directly for where clauses and generics, as PContainer/PEnum doesn't store them
27
    let where_clauses_tokens =
230✔
28
        build_where_clauses(parsed.clauses.as_ref(), parsed.generics.as_ref(), opaque);
230✔
29
    let type_params = build_type_params(parsed.generics.as_ref(), opaque);
230✔
30

31
    // Container-level docs from PAttrs
32
    let maybe_container_doc = match &pe.container.attrs.doc[..] {
230✔
33
        [] => quote! {},
230✔
34
        doc_lines => quote! { .doc(&[#(#doc_lines),*]) },
4✔
35
    };
36

37
    let container_attributes_tokens = {
230✔
38
        let mut attribute_tokens: Vec<TokenStream> = Vec::new();
230✔
39
        for attr in &pe.container.attrs.facet {
230✔
40
            // All attributes go through grammar dispatch
36✔
41
            let ext_attr = emit_attr(attr);
36✔
42
            attribute_tokens.push(quote! { #ext_attr });
36✔
43
        }
36✔
44

45
        if attribute_tokens.is_empty() {
230✔
46
            quote! {}
202✔
47
        } else {
48
            quote! { .attributes(&const { [#(#attribute_tokens),*] }) }
28✔
49
        }
50
    };
51

52
    let type_tag_maybe = {
230✔
53
        if let Some(type_tag) = pe.container.attrs.get_builtin_args("type_tag") {
230✔
54
            quote! { .type_tag(#type_tag) }
×
55
        } else {
56
            quote! {}
230✔
57
        }
58
    };
59

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

63
    // Are these relevant for enums? Or is it always `repr(C)` if a `PrimitiveRepr` is present?
64
    let repr = match &valid_repr {
230✔
65
        PRepr::Transparent => unreachable!("this should be caught by PRepr::parse"),
×
66
        PRepr::Rust(_) => quote! { ::facet::Repr::default() },
193✔
67
        PRepr::C(_) => quote! { ::facet::Repr::c() },
37✔
68
    };
69

70
    // Helper for EnumRepr TS (token stream) generation for primitives
71
    fn enum_repr_ts_from_primitive(primitive_repr: PrimitiveRepr) -> TokenStream {
195✔
72
        let type_name_str = primitive_repr.type_name().to_string();
195✔
73
        let enum_repr_variant_ident = format_ident!("{}", type_name_str.to_uppercase());
195✔
74
        quote! { ::facet::EnumRepr::#enum_repr_variant_ident }
195✔
75
    }
195✔
76

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

94
            let repr_attr_content = match prim_opt {
37✔
95
                Some(p) => p.type_name(),
2✔
96
                None => quote! { C },
35✔
97
            };
98
            let mut shadow_defs = vec![quote! {
37✔
99
                #[repr(#repr_attr_content)]
100
                #[allow(dead_code)]
101
                enum #shadow_discriminant_name { #(#all_variant_names),* }
102
            }];
103

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

123
            shadow_defs.push(quote! {
37✔
124
                #[repr(C)]
125
                #[allow(non_snake_case, dead_code)]
126
                union #shadow_union_name #bgp_with_bounds #where_clauses_tokens { #(#all_union_fields),* }
127
            });
128

129
            // Shadow repr struct for enum as a whole
130
            let shadow_repr_name =
37✔
131
                quote::format_ident!("__Shadow_CRepr_Struct_for_{}", enum_name_str);
37✔
132
            shadow_defs.push(quote! {
37✔
133
                #[repr(C)]
134
                #[allow(non_snake_case)]
135
                #[allow(dead_code)]
136
                struct #shadow_repr_name #bgp_with_bounds #where_clauses_tokens {
137
                    _discriminant: #shadow_discriminant_name,
138
                    _phantom: #phantom_data,
139
                    _fields: #shadow_union_name #bgp_without_bounds,
140
                }
141
            });
142

143
            // Generate variant_expressions
144
            let mut discriminant: Option<&TokenStream> = None;
37✔
145
            let mut discriminant_offset: i64 = 0;
37✔
146
            let mut exprs = Vec::new();
37✔
147

148
            for pv in pe.variants.iter() {
90✔
149
                if let Some(dis) = &pv.discriminant {
90✔
150
                    discriminant = Some(dis);
×
151
                    discriminant_offset = 0;
×
152
                }
90✔
153

154
                let discriminant_ts = if let Some(discriminant) = discriminant {
90✔
155
                    if discriminant_offset > 0 {
×
156
                        quote! { #discriminant + #discriminant_offset }
×
157
                    } else {
158
                        quote! { #discriminant }
×
159
                    }
160
                } else {
161
                    quote! { #discriminant_offset }
90✔
162
                };
163

164
                let display_name = pv.name.effective.clone();
90✔
165
                let variant_attrs_tokens = {
90✔
166
                    let name_token = TokenTree::Literal(Literal::string(&display_name));
90✔
167
                    // All attributes go through grammar dispatch
168
                    if pv.attrs.facet.is_empty() {
90✔
169
                        quote! { .name(#name_token) }
90✔
170
                    } else {
NEW
171
                        let attrs_list: Vec<TokenStream> = pv
×
NEW
172
                            .attrs
×
NEW
173
                            .facet
×
NEW
174
                            .iter()
×
NEW
175
                            .map(|attr| {
×
NEW
176
                                let ext_attr = emit_attr(attr);
×
NEW
177
                                quote! { #ext_attr }
×
NEW
178
                            })
×
NEW
179
                            .collect();
×
NEW
180
                        quote! { .name(#name_token).attributes(&const { [#(#attrs_list),*] }) }
×
181
                    }
182
                };
183

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

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

288
                        // Handle empty fields case explicitly
289
                        let struct_fields = if fields_with_types.is_empty() {
38✔
290
                            // Only add phantom data for empty struct variants
291
                            quote! { _phantom: #phantom_data }
×
292
                        } else {
293
                            // Add fields plus phantom data for non-empty struct variants
294
                            quote! { #(#fields_with_types),*, _phantom: #phantom_data }
38✔
295
                        };
296
                        shadow_defs.push(quote! {
38✔
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
38✔
305
                            .iter()
38✔
306
                            .map(|pf| {
65✔
307
                                gen_field_from_pfield(
65✔
308
                                    pf,
65✔
309
                                    &shadow_struct_name,
65✔
310
                                    &facet_bgp,
65✔
311
                                    Some(variant_offset.clone()),
65✔
312
                                )
313
                            })
65✔
314
                            .collect();
38✔
315

316
                        exprs.push(quote! {{
38✔
317
                            let fields: &'static [::facet::Field] = &const {[
318
                                #(#field_defs),*
319
                            ]};
320
                            ::facet::Variant::builder()
321
                                #variant_attrs_tokens
322
                                .discriminant(#discriminant_ts as i64)
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;
90✔
332
            }
333

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

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

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

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

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

364
                let discriminant_ts = if let Some(discriminant) = discriminant {
431✔
365
                    if discriminant_offset > 0 {
26✔
366
                        quote! { #discriminant + #discriminant_offset }
×
367
                    } else {
368
                        quote! { #discriminant }
26✔
369
                    }
370
                } else {
371
                    quote! { #discriminant_offset }
405✔
372
                };
373

374
                let display_name = pv.name.effective.clone();
431✔
375
                let variant_attrs_tokens = {
431✔
376
                    let name_token = TokenTree::Literal(Literal::string(&display_name));
431✔
377
                    // All attributes go through grammar dispatch
378
                    if pv.attrs.facet.is_empty() {
431✔
379
                        quote! { .name(#name_token) }
427✔
380
                    } else {
381
                        let attrs_list: Vec<TokenStream> = pv
4✔
382
                            .attrs
4✔
383
                            .facet
4✔
384
                            .iter()
4✔
385
                            .map(|attr| {
4✔
386
                                let ext_attr = emit_attr(attr);
4✔
387
                                quote! { #ext_attr }
4✔
388
                            })
4✔
389
                            .collect();
4✔
390
                        quote! { .name(#name_token).attributes(&const { [#(#attrs_list),*] }) }
4✔
391
                    }
392
                };
393

394
                let maybe_doc = match &pv.attrs.doc[..] {
431✔
395
                    [] => quote! {},
431✔
396
                    doc_lines => quote! { .doc(&[#(#doc_lines),*]) },
9✔
397
                };
398

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

541
    // Only make static_decl for non-generic enums
542
    let static_decl = if parsed.generics.is_none() {
230✔
543
        generate_static_decl(enum_name)
221✔
544
    } else {
545
        quote! {}
9✔
546
    };
547

548
    // Set up generics for impl blocks
549
    let facet_bgp = bgp.with_lifetime(LifetimeName(format_ident!("__facet")));
230✔
550
    let bgp_def = facet_bgp.display_with_bounds();
230✔
551
    let bgp_without_bounds = bgp.display_without_bounds();
230✔
552

553
    let (ty, fields) = if opaque {
230✔
554
        (
×
555
            quote! {
×
556
                .ty(::facet::Type::User(::facet::UserType::Opaque))
×
557
            },
×
558
            quote! {},
×
559
        )
×
560
    } else {
561
        (
562
            quote! {
230✔
563
                .ty(::facet::Type::User(::facet::UserType::Enum(::facet::EnumType::builder()
564
                        // Use variant expressions that just reference the shadow structs
565
                        // which are now defined above
566
                        .variants(__facet_variants)
567
                        .repr(#repr)
568
                        .enum_repr(#enum_repr_type_tokenstream)
569
                        .build())
570
                ))
571
            },
572
            quote! {
230✔
573
                let __facet_variants: &'static [::facet::Variant] = &const {[
574
                    #(#variant_expressions),*
575
                ]};
576
            },
577
        )
578
    };
579

580
    // Generate the impl
581
    quote! {
230✔
582
        #static_decl
583

584
        #[automatically_derived]
585
        #[allow(non_camel_case_types)]
586
        unsafe impl #bgp_def ::facet::Facet<'__facet> for #enum_name #bgp_without_bounds #where_clauses_tokens {
587
            const SHAPE: &'static ::facet::Shape = &const {
588
                #(#shadow_struct_defs)*
589
                #fields
590
                ::facet::Shape::builder_for_sized::<Self>()
591
                    .vtable({
592
                        ::facet::value_vtable!(Self, #type_name_fn)
593
                    })
594
                    .type_identifier(#enum_name_str)
595
                    #type_params
596
                    #ty
597
                    #maybe_container_doc
598
                    #container_attributes_tokens
599
                    #type_tag_maybe
600
                    .build()
601
            };
602
        }
603
    }
604
}
230✔
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