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

facet-rs / facet / 14703618340

28 Apr 2025 08:28AM UTC coverage: 58.217% (+0.5%) from 57.737%
14703618340

Pull #452

github

web-flow
Merge 0cf745b94 into 2b441c6ce
Pull Request #452: support serialize flattening

727 of 1043 new or added lines in 9 files covered. (69.7%)

2 existing lines in 1 file now uncovered.

7609 of 13070 relevant lines covered (58.22%)

77.37 hits per line

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

92.4
/facet-derive-emit/src/process_enum.rs
1
use super::normalize_ident_str;
2
use super::*;
3

4
/// Process a variant name, applying rename attribute or rename_all rule
5
fn process_variant_name(
186✔
6
    variant_name: &str,
186✔
7
    attributes: &[Attribute],
186✔
8
    rename_rule: RenameRule,
186✔
9
) -> String {
186✔
10
    for attr in attributes {
221✔
11
        if let AttributeInner::Facet(facet_attr) = &attr.body.content {
44✔
12
            if let FacetInner::Other(tt) = &facet_attr.inner.content {
12✔
13
                let attr_str = tt.tokens_to_string();
12✔
14
                if let Some(equal_pos) = attr_str.find('=') {
12✔
15
                    let key = attr_str[..equal_pos].trim();
11✔
16
                    if key == "rename" {
11✔
17
                        let value = attr_str[equal_pos + 1..].trim().trim_matches('"');
9✔
18
                        return value.to_string();
9✔
19
                    }
2✔
20
                }
1✔
21
            }
×
22
        }
32✔
23
    }
24

25
    let mut final_name = variant_name.to_string();
177✔
26
    if rename_rule != RenameRule::Passthrough {
177✔
27
        final_name = rename_rule.apply(variant_name);
7✔
28
    }
170✔
29

30
    final_name
177✔
31
}
186✔
32

33
// mirrors facet_core::types::EnumRepr
34
#[derive(Clone, Copy)]
35
enum Discriminant {
36
    U8,
37
    U16,
38
    U32,
39
    U64,
40
    USize,
41
    I8,
42
    I16,
43
    I32,
44
    I64,
45
    ISize,
46
}
47

48
impl Discriminant {
49
    fn as_enum_repr(&self) -> &'static str {
56✔
50
        match self {
56✔
51
            Discriminant::U8 => "U8",
49✔
52
            Discriminant::U16 => "U16",
4✔
53
            Discriminant::U32 => "U32",
2✔
54
            Discriminant::U64 => "U64",
×
55
            Discriminant::USize => "USize",
×
56
            Discriminant::I8 => "I8",
×
57
            Discriminant::I16 => "I16",
1✔
58
            Discriminant::I32 => "I32",
×
59
            Discriminant::I64 => "I64",
×
60
            Discriminant::ISize => "ISize",
×
61
        }
62
    }
56✔
63

64
    fn as_rust_type(&self) -> &'static str {
80✔
65
        match self {
80✔
66
            Discriminant::U8 => "u8",
68✔
67
            Discriminant::U16 => "u16",
9✔
68
            Discriminant::U32 => "u32",
2✔
69
            Discriminant::U64 => "u64",
×
70
            Discriminant::USize => "usize",
×
71
            Discriminant::I8 => "i8",
×
72
            Discriminant::I16 => "i16",
1✔
73
            Discriminant::I32 => "i32",
×
74
            Discriminant::I64 => "i64",
×
75
            Discriminant::ISize => "isize",
×
76
        }
77
    }
80✔
78
}
79

80
struct ProcessedEnumBody {
81
    shadow_struct_defs: Vec<String>,
82
    variant_expressions: Vec<String>,
83
    repr_type: String,
84
}
85

86
type EnumVariant = Delimited<EnumVariantLike, Comma>;
87

88
/// Processes an enum to implement Facet
89
///
90
/// Example input:
91
/// ```rust
92
/// #[repr(u8)]
93
/// enum Color {
94
///     Red,
95
///     Green,
96
///     Blue(u8, u8),
97
///     Custom { r: u8, g: u8, b: u8 }
98
/// }
99
/// ```
100
pub(crate) fn process_enum(parsed: Enum) -> TokenStream {
70✔
101
    let enum_name = parsed.name.to_string();
70✔
102
    let bgp = BoundedGenericParams::parse(parsed.generics.as_ref());
70✔
103
    let where_clauses = build_where_clauses(parsed.clauses.as_ref(), parsed.generics.as_ref());
70✔
104
    let type_params = build_type_params(parsed.generics.as_ref());
70✔
105
    let container_attributes = build_container_attributes(&parsed.attributes);
70✔
106

70✔
107
    // collect all `#repr(..)` attrs
70✔
108
    // either multiple attrs, or a single attr with multiple values
70✔
109
    let attr_iter = parsed
70✔
110
        .attributes
70✔
111
        .iter()
70✔
112
        .filter_map(|attr| {
122✔
113
            if let AttributeInner::Repr(repr_attr) = &attr.body.content {
122✔
114
                if repr_attr.attr.content.0.is_empty() {
70✔
115
                    // treat empty repr as non-existent
116
                    // (this shouldn't be possible, but just in case)
117
                    None
×
118
                } else {
119
                    Some(repr_attr)
70✔
120
                }
121
            } else {
122
                None
52✔
123
            }
124
        })
122✔
125
        .flat_map(|repr_attr| repr_attr.attr.content.0.iter());
70✔
126

127
    let mut repr_c = false;
70✔
128
    let mut discriminant_type = None;
70✔
129

130
    for attr in attr_iter {
142✔
131
        let attr = attr.value.to_string();
72✔
132
        match attr.as_str() {
72✔
133
            // this is #[repr(C)]
72✔
134
            "C" => repr_c = true,
72✔
135

136
            // set the repr type
137
            // NOTE: we're not worried about multiple
138
            // clashing types here -- that's rustc's problem
139
            "u8" => discriminant_type = Some(Discriminant::U8),
56✔
140
            "u16" => discriminant_type = Some(Discriminant::U16),
7✔
141
            "u32" => discriminant_type = Some(Discriminant::U32),
3✔
142
            "u64" => discriminant_type = Some(Discriminant::U64),
1✔
143
            "usize" => discriminant_type = Some(Discriminant::USize),
1✔
144
            "i8" => discriminant_type = Some(Discriminant::I8),
1✔
145
            "i16" => discriminant_type = Some(Discriminant::I16),
1✔
146
            "i32" => discriminant_type = Some(Discriminant::I32),
×
147
            "i64" => discriminant_type = Some(Discriminant::I64),
×
148
            "isize" => discriminant_type = Some(Discriminant::ISize),
×
149
            _ => {
150
                return r#"compile_error!("Facet only supports enums with a primitive representation (e.g. #[repr(u8)]) or C-style (e.g. #[repr(C)]")"#
×
151
            .into_token_stream()
×
152
            }
153
        }
154
    }
155

156
    let processed_body = match (repr_c, discriminant_type) {
70✔
157
        (true, _) => {
158
            // C-style enum, no discriminant type
159
            process_c_style_enum(
16✔
160
                &enum_name,
16✔
161
                &parsed.body.content.0,
16✔
162
                discriminant_type,
16✔
163
                &bgp,
16✔
164
                &where_clauses,
16✔
165
                container_attributes.rename_rule,
16✔
166
            )
167
        }
168
        (false, Some(discriminant_type)) => process_primitive_enum(
54✔
169
            &enum_name,
54✔
170
            &parsed.body.content.0,
54✔
171
            discriminant_type,
54✔
172
            &bgp,
54✔
173
            &where_clauses,
54✔
174
            container_attributes.rename_rule,
54✔
175
        ),
176
        _ => {
177
            return r#"compile_error!("Enums must have an explicit representation (e.g. #[repr(u8)] or #[repr(C)]) to be used with Facet")"#
×
178
            .into_token_stream()
×
179
        }
180
    };
181

182
    let ProcessedEnumBody {
183
        shadow_struct_defs,
70✔
184
        variant_expressions,
70✔
185
        repr_type,
70✔
186
    } = processed_body;
70✔
187

70✔
188
    // Join the shadow struct definitions and variant expressions
70✔
189
    let shadow_structs = shadow_struct_defs.join("\n\n");
70✔
190
    let variants = variant_expressions.join(", ");
70✔
191

192
    let static_decl = if parsed.generics.is_none() {
70✔
193
        generate_static_decl(&enum_name)
58✔
194
    } else {
195
        String::new()
12✔
196
    };
197
    let maybe_container_doc = build_maybe_doc(&parsed.attributes);
70✔
198

70✔
199
    // Generate the impl
70✔
200
    let output = format!(
70✔
201
        r#"
70✔
202
{static_decl}
70✔
203

70✔
204
#[automatically_derived]
70✔
205
unsafe impl{bgp_def} ::facet::Facet<'__facet> for {enum_name}{bgp_without_bounds} {where_clauses} {{
70✔
206
    const SHAPE: &'static ::facet::Shape = &const {{
70✔
207
        // Define all shadow structs at the beginning of the const block
70✔
208
        // to ensure they're in scope for offset_of! macros
70✔
209
        {shadow_structs}
70✔
210

70✔
211
        let __facet_variants: &'static [::facet::Variant] = &const {{[
70✔
212
            {variants}
70✔
213
        ]}};
70✔
214

70✔
215
        ::facet::Shape::builder()
70✔
216
            .id(::facet::ConstTypeId::of::<Self>())
70✔
217
            .layout(::core::alloc::Layout::new::<Self>())
70✔
218
            {type_params}
70✔
219
            .vtable(&const {{ ::facet::value_vtable!(
70✔
220
                Self,
70✔
221
                |f, _opts| ::core::fmt::Write::write_str(f, "{enum_name}")
70✔
222
            )}})
70✔
223
            .def(::facet::Def::Enum(::facet::EnumDef::builder()
70✔
224
                // Use variant expressions that just reference the shadow structs
70✔
225
                // which are now defined above
70✔
226
                .variants(__facet_variants)
70✔
227
                .repr(::facet::EnumRepr::{repr_type})
70✔
228
                .build()))
70✔
229
            {maybe_container_doc}
70✔
230
            {container_attributes}
70✔
231
            .build()
70✔
232
    }};
70✔
233
}}
70✔
234
        "#,
70✔
235
        bgp_def = bgp.with_lifetime("__facet").display_with_bounds(),
70✔
236
        bgp_without_bounds = bgp.display_without_bounds(),
70✔
237
        container_attributes = container_attributes.code,
70✔
238
    );
70✔
239

70✔
240
    // Uncomment to see generated code before lexin
70✔
241
    // panic!("output =\n{output}");
70✔
242

70✔
243
    // Return the generated code
70✔
244
    output.into_token_stream()
70✔
245
}
70✔
246

247
/// C-style enums (i.e. #[repr(C)], #[repr(C, u*)] and #[repr(C, i*)]) are laid out
248
/// as a #[repr(C)] struct with two fields: the discriminant and the union of all the variants.
249
///
250
/// See: <https://doc.rust-lang.org/reference/type-layout.html#r-layout.repr.primitive.adt>
251
///
252
/// To calculate the offsets of each variant, we create a shadow struct that mimics this
253
/// structure and use the `offset_of!` macro to calculate the offsets of each field.
254
fn process_c_style_enum(
16✔
255
    enum_name: &str,
16✔
256
    variants: &[EnumVariant],
16✔
257
    discriminant_type: Option<Discriminant>,
16✔
258
    bgp: &BoundedGenericParams,
16✔
259
    where_clauses: &str,
16✔
260
    rename_rule: RenameRule,
16✔
261
) -> ProcessedEnumBody {
16✔
262
    let facet_bgp = bgp.with(BoundedGenericParam {
16✔
263
        bounds: None,
16✔
264
        param: GenericParamName::Lifetime("__facet".into()),
16✔
265
    });
16✔
266

16✔
267
    // Collect shadow struct definitions separately from variant expressions
16✔
268
    let mut shadow_struct_defs = Vec::new();
16✔
269
    let mut variant_expressions = Vec::new();
16✔
270

16✔
271
    // first, create an enum to represent the discriminant type
16✔
272
    let shadow_discriminant_name = format!("__ShadowDiscriminant{enum_name}");
16✔
273
    let all_variant_names = variants
16✔
274
        .iter()
16✔
275
        .map(|var_like| match &var_like.value.variant {
44✔
276
            EnumVariantData::Unit(unit) => unit.name.to_string(),
10✔
277
            EnumVariantData::Tuple(tuple) => tuple.name.to_string(),
21✔
278
            EnumVariantData::Struct(struct_var) => struct_var.name.to_string(),
13✔
279
        })
44✔
280
        .collect::<Vec<_>>()
16✔
281
        .join(", ");
16✔
282
    shadow_struct_defs.push(format!(
16✔
283
        "#[repr({repr})] enum {shadow_discriminant_name} {{ {all_variant_names} }}",
16✔
284
        // repr is either C or the explicit discriminant type
16✔
285
        repr = discriminant_type.map(|d| d.as_rust_type()).unwrap_or("C")
16✔
286
    ));
16✔
287

16✔
288
    // we'll also generate a shadow union for the fields
16✔
289
    let shadow_union_name = format!("__ShadowFields{enum_name}");
16✔
290

16✔
291
    let all_union_fields = variants
16✔
292
        .iter()
16✔
293
        .map(|var_like| match &var_like.value.variant {
44✔
294
            EnumVariantData::Unit(unit) => unit.name.to_string(),
10✔
295
            EnumVariantData::Tuple(tuple) => tuple.name.to_string(),
21✔
296
            EnumVariantData::Struct(struct_var) => struct_var.name.to_string(),
13✔
297
        })
44✔
298
        .map(|variant_name| {
44✔
299
            format!(
44✔
300
                "{variant_name}: std::mem::ManuallyDrop<__ShadowField{enum_name}_{variant_name}{bgp}>",
44✔
301
                bgp = facet_bgp.display_without_bounds()
44✔
302
            )
44✔
303
        })
44✔
304
        .collect::<Vec<_>>()
16✔
305
        .join(", ");
16✔
306

16✔
307
    shadow_struct_defs.push(format!(
16✔
308
        "#[repr(C)] union {shadow_union_name}{bgp} {where_clauses} {{ {all_union_fields} }}",
16✔
309
        bgp = facet_bgp.display_with_bounds()
16✔
310
    ));
16✔
311

16✔
312
    // Create a shadow struct to represent the enum layout
16✔
313
    let shadow_repr_name = format!("__ShadowRepr{enum_name}");
16✔
314

16✔
315
    shadow_struct_defs.push(format!(
16✔
316
        "#[repr(C)] struct {shadow_repr_name}{struct_bgp} {where_clauses} {{
16✔
317
            _discriminant: {shadow_discriminant_name},
16✔
318
            _phantom: {phantom},
16✔
319
            _fields: {shadow_union_name}{fields_bgp},
16✔
320
        }}",
16✔
321
        struct_bgp = facet_bgp.display_with_bounds(),
16✔
322
        fields_bgp = facet_bgp.display_without_bounds(),
16✔
323
        phantom = facet_bgp.display_as_phantom_data(),
16✔
324
    ));
16✔
325

16✔
326
    // Discriminant values are either manually defined, or incremented from the last one
16✔
327
    // See: <https://doc.rust-lang.org/reference/items/enumerations.html#implicit-discriminants>
16✔
328
    let mut discriminant_value = 0;
16✔
329
    for var_like in variants.iter() {
44✔
330
        if let Some(x) = &var_like.value.discriminant {
44✔
331
            discriminant_value = get_discriminant_value(&x.second);
×
332
        }
44✔
333
        match &var_like.value.variant {
44✔
334
            EnumVariantData::Unit(unit) => {
10✔
335
                let variant_name = unit.name.to_string();
10✔
336
                let display_name =
10✔
337
                    process_variant_name(&variant_name, &unit.attributes, rename_rule);
10✔
338
                let attributes = variant_attrs(&unit.attributes);
10✔
339
                let maybe_doc = build_maybe_doc(&unit.attributes);
10✔
340

10✔
341
                // Generate shadow struct for this tuple variant to calculate offsets
10✔
342
                let shadow_struct_name = format!("__ShadowField{enum_name}_{variant_name}");
10✔
343

10✔
344
                // Add shadow struct definition
10✔
345
                shadow_struct_defs.push(format!(
10✔
346
                    "#[repr(C)] struct {shadow_struct_name}{bgp} {where_clauses} {{ _phantom: {phantom} }}",
10✔
347
                    bgp = facet_bgp.display_with_bounds(),
10✔
348
                    phantom = facet_bgp.display_as_phantom_data(),
10✔
349
                ));
10✔
350

10✔
351
                // variant offset is offset of the `_fields` union
10✔
352
                variant_expressions.push(format!(
10✔
353
                    "::facet::Variant::builder()
10✔
354
                    .name({display_name:?})
10✔
355
                    .discriminant({discriminant_value})
10✔
356
                    .fields(::facet::StructDef::builder().unit().build())
10✔
357
                    .attributes(&const {{ [{attributes}] }})
10✔
358
                    {maybe_doc}
10✔
359
                    .build()",
10✔
360
                ));
10✔
361
            }
10✔
362
            EnumVariantData::Tuple(tuple) => {
21✔
363
                let variant_name = tuple.name.to_string();
21✔
364
                let display_name =
21✔
365
                    process_variant_name(&variant_name, &tuple.attributes, rename_rule);
21✔
366
                let attributes = variant_attrs(&tuple.attributes);
21✔
367
                let maybe_doc = build_maybe_doc(&tuple.attributes);
21✔
368

21✔
369
                // Generate shadow struct for this tuple variant to calculate offsets
21✔
370
                let shadow_struct_name = format!("__ShadowField{enum_name}_{variant_name}");
21✔
371

21✔
372
                // Build the list of fields and types for the shadow struct
21✔
373
                let fields_with_types = tuple
21✔
374
                    .fields
21✔
375
                    .content
21✔
376
                    .0
21✔
377
                    .iter()
21✔
378
                    .enumerate()
21✔
379
                    .map(|(idx, field)| {
32✔
380
                        let typ = VerbatimDisplay(&field.value.typ).to_string();
32✔
381
                        format!("_{}: {}", idx, typ)
32✔
382
                    })
32✔
383
                    .collect::<Vec<String>>()
21✔
384
                    .join(", ");
21✔
385

21✔
386
                // Add shadow struct definition
21✔
387
                shadow_struct_defs.push(format!(
21✔
388
                    "#[repr(C)] struct {shadow_struct_name}{bgp} {where_clauses} {{ {fields_with_types}, _phantom: {phantom} }}",
21✔
389
                    bgp = facet_bgp.display_with_bounds(),
21✔
390
                    phantom = facet_bgp.display_as_phantom_data(),
21✔
391
                ));
21✔
392

21✔
393
                let variant_offset = format!(
21✔
394
                    "::core::mem::offset_of!({shadow_repr_name}{facet_bgp_use}, _fields)",
21✔
395
                    facet_bgp_use = facet_bgp.display_without_bounds()
21✔
396
                );
21✔
397

21✔
398
                // Build the list of field types with calculated offsets
21✔
399
                let fields = tuple
21✔
400
                    .fields
21✔
401
                    .content
21✔
402
                    .0
21✔
403
                    .iter()
21✔
404
                    .enumerate()
21✔
405
                    .map(|(idx, field)| {
32✔
406
                        let field_name = format!("_{idx}");
32✔
407
                        gen_struct_field(FieldInfo {
32✔
408
                            raw_field_name: &field_name,
32✔
409
                            normalized_field_name: &field_name,
32✔
410
                            field_type: &field.value.typ.tokens_to_string(),
32✔
411
                            struct_name: &shadow_struct_name,
32✔
412
                            bgp: &facet_bgp,
32✔
413
                            attrs: &field.value.attributes,
32✔
414
                            base_field_offset: Some(&variant_offset),
32✔
415
                            rename_rule,
32✔
416
                        })
32✔
417
                    })
32✔
418
                    .collect::<Vec<String>>()
21✔
419
                    .join(", ");
21✔
420

21✔
421
                // Add variant expression - now with discriminant
21✔
422
                variant_expressions.push(format!(
21✔
423
                    "{{
21✔
424
                        let fields: &'static [::facet::Field] = &const {{[
21✔
425
                            {fields}
21✔
426
                        ]}};
21✔
427

21✔
428
                        ::facet::Variant::builder()
21✔
429
                            .name({display_name:?})
21✔
430
                            .discriminant({discriminant_value})
21✔
431
                            .fields(::facet::StructDef::builder().tuple().fields(fields).build())
21✔
432
                            .attributes(&const {{ [{attributes}] }})
21✔
433
                            {maybe_doc}
21✔
434
                            .build()
21✔
435
                    }}",
21✔
436
                ));
437
            }
438
            EnumVariantData::Struct(struct_var) => {
13✔
439
                let variant_name = struct_var.name.to_string();
13✔
440
                let display_name =
13✔
441
                    process_variant_name(&variant_name, &struct_var.attributes, rename_rule);
13✔
442
                let attributes = variant_attrs(&struct_var.attributes);
13✔
443
                let maybe_doc = build_maybe_doc(&struct_var.attributes);
13✔
444

13✔
445
                // Generate shadow struct for this struct variant to calculate offsets
13✔
446
                let shadow_struct_name = format!("__ShadowField{enum_name}_{variant_name}");
13✔
447

13✔
448
                // Build the list of fields and types
13✔
449
                let fields_with_types = struct_var
13✔
450
                    .fields
13✔
451
                    .content
13✔
452
                    .0
13✔
453
                    .iter()
13✔
454
                    .map(|field| {
27✔
455
                        let name = field.value.name.to_string();
27✔
456
                        let typ = VerbatimDisplay(&field.value.typ).to_string();
27✔
457
                        format!("{}: {}", name, typ)
27✔
458
                    })
27✔
459
                    .collect::<Vec<String>>()
13✔
460
                    .join(", ");
13✔
461

13✔
462
                // Add shadow struct definition
13✔
463
                shadow_struct_defs.push(format!(
13✔
464
                    "#[repr(C)] struct {shadow_struct_name}{bgp} {where_clauses} {{ {fields_with_types}, _phantom: {phantom} }}",
13✔
465
                    bgp = facet_bgp.display_with_bounds(),
13✔
466
                    phantom = facet_bgp.display_as_phantom_data(),
13✔
467
                ));
13✔
468

13✔
469
                let variant_offset = format!(
13✔
470
                    "::core::mem::offset_of!({shadow_repr_name}{facet_bgp_use}, _fields)",
13✔
471
                    facet_bgp_use = facet_bgp.display_without_bounds()
13✔
472
                );
13✔
473

13✔
474
                // Build the list of field types with calculated offsets
13✔
475
                let fields = struct_var
13✔
476
                    .fields
13✔
477
                    .content
13✔
478
                    .0
13✔
479
                    .iter()
13✔
480
                    .map(|field| {
27✔
481
                        // Handle raw identifiers (like r#type) by stripping the 'r#' prefix.
27✔
482
                        let raw_field_name = field.value.name.to_string(); // e.g., "r#type"
27✔
483
                        let normalized_field_name = normalize_ident_str(&raw_field_name); // e.g., "type"
27✔
484
                        let field_type = field.value.typ.tokens_to_string();
27✔
485
                        gen_struct_field(FieldInfo {
27✔
486
                            raw_field_name: &raw_field_name,
27✔
487
                            normalized_field_name,
27✔
488
                            field_type: &field_type,
27✔
489
                            struct_name: &shadow_struct_name,
27✔
490
                            bgp: &facet_bgp,
27✔
491
                            attrs: &field.value.attributes,
27✔
492
                            base_field_offset: Some(&variant_offset),
27✔
493
                            rename_rule,
27✔
494
                        })
27✔
495
                    })
27✔
496
                    .collect::<Vec<String>>()
13✔
497
                    .join(", ");
13✔
498

13✔
499
                // Add variant expression - now with discriminant
13✔
500
                variant_expressions.push(format!(
13✔
501
                    "{{
13✔
502
                        let fields: &'static [::facet::Field] = &const {{[
13✔
503
                            {fields}
13✔
504
                        ]}};
13✔
505

13✔
506
                        ::facet::Variant::builder()
13✔
507
                            .name({display_name:?})
13✔
508
                            .discriminant({discriminant_value})
13✔
509
                            .fields(::facet::StructDef::builder().struct_().fields(fields).build())
13✔
510
                            .attributes(&const {{ [{attributes}] }})
13✔
511
                            {maybe_doc}
13✔
512
                            .build()
13✔
513
                    }}",
13✔
514
                ));
515
            }
516
        }
517
        discriminant_value += 1;
44✔
518
    }
519

520
    ProcessedEnumBody {
521
        shadow_struct_defs,
16✔
522
        variant_expressions,
16✔
523
        repr_type: discriminant_type.map_or_else(
16✔
524
            || format!("from_discriminant_size::<{shadow_discriminant_name}>()"),
14✔
525
            |d| d.as_enum_repr().to_string(),
2✔
526
        ),
527
    }
528
}
16✔
529

530
/// Primitive enums (i.e. #[repr(u*)] and #[repr(i*)]) are laid out
531
/// as a union of all the variants, with the discriminant as an "inner" tag in the struct.
532
///
533
/// See: <https://doc.rust-lang.org/reference/type-layout.html#r-layout.repr.primitive.adt>
534
///
535
/// To calculate the offsets of each variant, we create a shadow struct that mimics this
536
/// structure and use the `offset_of!` macro to calculate the offsets of each field.
537
fn process_primitive_enum(
54✔
538
    enum_name: &str,
54✔
539
    variants: &[EnumVariant],
54✔
540
    discriminant_type: Discriminant,
54✔
541
    bgp: &BoundedGenericParams,
54✔
542
    where_clauses: &str,
54✔
543
    rename_rule: RenameRule,
54✔
544
) -> ProcessedEnumBody {
54✔
545
    let facet_bgp = bgp.with(BoundedGenericParam {
54✔
546
        bounds: None,
54✔
547
        param: GenericParamName::Lifetime("__facet".into()),
54✔
548
    });
54✔
549

54✔
550
    // Collect shadow struct definitions separately from variant expressions
54✔
551
    let mut shadow_struct_defs = Vec::new();
54✔
552
    let mut variant_expressions = Vec::new();
54✔
553

54✔
554
    // Discriminant values are either manually defined, or incremented from the last one
54✔
555
    // See: <https://doc.rust-lang.org/reference/items/enumerations.html#implicit-discriminants>
54✔
556
    let mut discriminant_value = 0;
54✔
557
    for var_like in variants.iter() {
142✔
558
        if let Some(x) = &var_like.value.discriminant {
142✔
559
            discriminant_value = get_discriminant_value(&x.second);
17✔
560
        }
125✔
561
        match &var_like.value.variant {
142✔
562
            EnumVariantData::Unit(unit) => {
64✔
563
                let variant_name = unit.name.to_string();
64✔
564
                let display_name =
64✔
565
                    process_variant_name(&variant_name, &unit.attributes, rename_rule);
64✔
566
                let attributes = variant_attrs(&unit.attributes);
64✔
567
                let maybe_doc = build_maybe_doc(&unit.attributes);
64✔
568

64✔
569
                variant_expressions.push(format!(
64✔
570
                    "::facet::Variant::builder()
64✔
571
                    .name({display_name:?})
64✔
572
                    .discriminant({discriminant_value})
64✔
573
                    .fields(::facet::StructDef::builder().unit().build())
64✔
574
                    .attributes(&const {{ [{attributes}] }})
64✔
575
                    {maybe_doc}
64✔
576
                    .build()",
64✔
577
                ));
64✔
578
            }
64✔
579
            EnumVariantData::Tuple(tuple) => {
44✔
580
                let variant_name = tuple.name.to_string();
44✔
581
                let display_name =
44✔
582
                    process_variant_name(&variant_name, &tuple.attributes, rename_rule);
44✔
583
                let attributes = variant_attrs(&tuple.attributes);
44✔
584
                let maybe_doc = build_maybe_doc(&tuple.attributes);
44✔
585

44✔
586
                // Generate shadow struct for this tuple variant to calculate offsets
44✔
587
                let shadow_struct_name = format!("__Shadow{}_{}", enum_name, variant_name);
44✔
588

44✔
589
                // Build the list of fields and types for the shadow struct
44✔
590
                let fields_with_types = tuple
44✔
591
                    .fields
44✔
592
                    .content
44✔
593
                    .0
44✔
594
                    .iter()
44✔
595
                    .enumerate()
44✔
596
                    .map(|(idx, field)| {
58✔
597
                        let typ = VerbatimDisplay(&field.value.typ).to_string();
58✔
598
                        format!("_{}: {}", idx, typ)
58✔
599
                    })
58✔
600
                    .collect::<Vec<String>>()
44✔
601
                    .join(", ");
44✔
602

44✔
603
                // Add shadow struct definition
44✔
604
                shadow_struct_defs.push(format!(
44✔
605
                    "#[repr(C)] struct {shadow_struct_name}{bgp} {where_clauses}  {{
44✔
606
                        _discriminant: {discriminant},
44✔
607
                        _phantom: {phantom},
44✔
608
                        {fields_with_types}
44✔
609
                    }}",
44✔
610
                    bgp = facet_bgp.display_with_bounds(),
44✔
611
                    phantom = facet_bgp.display_as_phantom_data(),
44✔
612
                    discriminant = discriminant_type.as_rust_type(),
44✔
613
                ));
44✔
614

44✔
615
                // Build the list of field types with calculated offsets
44✔
616
                let fields = tuple
44✔
617
                    .fields
44✔
618
                    .content
44✔
619
                    .0
44✔
620
                    .iter()
44✔
621
                    .enumerate()
44✔
622
                    .map(|(idx, field)| {
58✔
623
                        let field_name = format!("_{idx}");
58✔
624
                        gen_struct_field(FieldInfo {
58✔
625
                            raw_field_name: &field_name,
58✔
626
                            normalized_field_name: &field_name,
58✔
627
                            field_type: &field.value.typ.tokens_to_string(),
58✔
628
                            struct_name: &shadow_struct_name,
58✔
629
                            bgp: &facet_bgp,
58✔
630
                            attrs: &field.value.attributes,
58✔
631
                            base_field_offset: None,
58✔
632
                            rename_rule,
58✔
633
                        })
58✔
634
                    })
58✔
635
                    .collect::<Vec<String>>()
44✔
636
                    .join(", ");
44✔
637

44✔
638
                // Add variant expression - now with discriminant
44✔
639
                variant_expressions.push(format!(
44✔
640
                    "{{
44✔
641
                        let fields: &'static [::facet::Field] = &const {{[
44✔
642
                            {fields}
44✔
643
                        ]}};
44✔
644

44✔
645
                        ::facet::Variant::builder()
44✔
646
                            .name({display_name:?})
44✔
647
                            .discriminant({discriminant_value})
44✔
648
                            .fields(::facet::StructDef::builder().tuple().fields(fields).build())
44✔
649
                            .attributes(&const {{ [{attributes}] }})
44✔
650
                            {maybe_doc}
44✔
651
                            .build()
44✔
652
                    }}",
44✔
653
                ));
654
            }
655
            EnumVariantData::Struct(struct_var) => {
34✔
656
                let variant_name = struct_var.name.to_string();
34✔
657
                let display_name =
34✔
658
                    process_variant_name(&variant_name, &struct_var.attributes, rename_rule);
34✔
659
                let attributes = variant_attrs(&struct_var.attributes);
34✔
660
                let maybe_doc = build_maybe_doc(&struct_var.attributes);
34✔
661

34✔
662
                // Generate shadow struct for this struct variant to calculate offsets
34✔
663
                let shadow_struct_name = format!("__Shadow{}_{}", enum_name, variant_name);
34✔
664

34✔
665
                // Build the list of fields and types
34✔
666
                let fields_with_types = struct_var
34✔
667
                    .fields
34✔
668
                    .content
34✔
669
                    .0
34✔
670
                    .iter()
34✔
671
                    .map(|field| {
60✔
672
                        let name = field.value.name.to_string();
60✔
673
                        let typ = VerbatimDisplay(&field.value.typ).to_string();
60✔
674
                        format!("{}: {}", name, typ)
60✔
675
                    })
60✔
676
                    .collect::<Vec<String>>()
34✔
677
                    .join(", ");
34✔
678

34✔
679
                // Add shadow struct definition
34✔
680
                shadow_struct_defs.push(format!(
34✔
681
                    "#[repr(C)] struct {shadow_struct_name}{bgp} {where_clauses} {{
34✔
682
                        _discriminant: {discriminant},
34✔
683
                        _phantom: {phantom},
34✔
684
                        {fields_with_types}
34✔
685
                    }}",
34✔
686
                    bgp = facet_bgp.display_with_bounds(),
34✔
687
                    phantom = facet_bgp.display_as_phantom_data(),
34✔
688
                    discriminant = discriminant_type.as_rust_type(),
34✔
689
                ));
34✔
690

34✔
691
                // Build the list of field types with calculated offsets
34✔
692
                let fields = struct_var
34✔
693
                    .fields
34✔
694
                    .content
34✔
695
                    .0
34✔
696
                    .iter()
34✔
697
                    .map(|field| {
60✔
698
                        // Handle raw identifiers (like r#type) by stripping the 'r#' prefix.
60✔
699
                        let raw_field_name = field.value.name.to_string(); // e.g., "r#type"
60✔
700
                        let normalized_field_name = normalize_ident_str(&raw_field_name); // e.g., "type"
60✔
701
                        gen_struct_field(FieldInfo {
60✔
702
                            raw_field_name: &raw_field_name,
60✔
703
                            normalized_field_name,
60✔
704
                            field_type: &field.value.typ.tokens_to_string(),
60✔
705
                            struct_name: &shadow_struct_name,
60✔
706
                            bgp: &facet_bgp,
60✔
707
                            attrs: &field.value.attributes,
60✔
708
                            base_field_offset: None,
60✔
709
                            rename_rule,
60✔
710
                        })
60✔
711
                    })
60✔
712
                    .collect::<Vec<String>>()
34✔
713
                    .join(", ");
34✔
714

34✔
715
                // Add variant expression - now with discriminant
34✔
716
                // variant offset is zero since all fields are
34✔
717
                // already computed relative to the discriminant
34✔
718
                variant_expressions.push(format!(
34✔
719
                    "{{
34✔
720
                        let fields: &'static [::facet::Field] = &const {{[
34✔
721
                            {fields}
34✔
722
                        ]}};
34✔
723

34✔
724
                        ::facet::Variant::builder()
34✔
725
                            .name({display_name:?})
34✔
726
                            .discriminant({discriminant_value})
34✔
727
                            .fields(::facet::StructDef::builder().struct_().fields(fields).build())
34✔
728
                            .attributes(&const {{ [{attributes}] }})
34✔
729
                            {maybe_doc}
34✔
730
                            .build()
34✔
731
                    }}",
34✔
732
                ));
733
            }
734
        }
735
        discriminant_value += 1;
142✔
736
    }
737

738
    ProcessedEnumBody {
54✔
739
        shadow_struct_defs,
54✔
740
        variant_expressions,
54✔
741
        repr_type: discriminant_type.as_enum_repr().to_string(),
54✔
742
    }
54✔
743
}
54✔
744

745
fn variant_attrs(attributes: &[Attribute]) -> String {
186✔
746
    attributes
186✔
747
        .iter()
186✔
748
        .filter_map(|attr| {
186✔
749
            if let AttributeInner::Facet(facet) = &attr.body.content {
44✔
750
                Some(facet)
12✔
751
            } else {
752
                None
32✔
753
            }
754
        })
44✔
755
        .map(|attr| {
186✔
756
            match &attr.inner.content {
12✔
NEW
757
                FacetInner::Sensitive(_sensitive) => {
×
NEW
758
                    r#"::facet::VariantAttribute::Sensitive"#.to_string()
×
759
                }
NEW
760
                FacetInner::Invariants(_invariants) => r#"compile_error!(
×
NEW
761
                    "`invariants` is not a valid attribute for an enum variant"
×
NEW
762
                )"#
×
NEW
763
                .to_string(),
×
NEW
764
                FacetInner::Opaque(_opaque) => r#"compile_error!(
×
NEW
765
                    "`opaque` is not a valid attribute for an enum variant"
×
NEW
766
                )"#
×
NEW
767
                .to_string(),
×
NEW
768
                FacetInner::DenyUnknownFields(_deny_unknown_fields) => r#"compile_error!(
×
NEW
769
                    "`deny_unknown_fields` is not a valid attribute for an enum variant"
×
NEW
770
                )"#
×
NEW
771
                .to_string(),
×
NEW
772
                FacetInner::DefaultEquals(_default_equals) => r#"compile_error!(
×
NEW
773
                    "`default_equals` is not a valid attribute for an enum variant"
×
NEW
774
                )"#
×
NEW
775
                .to_string(),
×
NEW
776
                FacetInner::Default(_default) => r#"compile_error!(
×
NEW
777
                    "`default` is not a valid attribute for an enum variant"
×
NEW
778
                )"#
×
NEW
779
                .to_string(),
×
NEW
780
                FacetInner::Transparent(_transparent) => r#"compile_error!(
×
NEW
781
                    "`transparent` is not a valid attribute for an enum variant"
×
NEW
782
                )"#
×
NEW
783
                .to_string(),
×
NEW
784
                FacetInner::RenameAll(_rename_all) => r#"compile_error!(
×
NEW
785
                    "`rename_all` is not a valid attribute for an enum variant"
×
NEW
786
                )"#
×
NEW
787
                .to_string(),
×
788
                FacetInner::Other(other) => {
12✔
789
                    format!(
12✔
790
                        r#"::facet::VariantAttribute::Arbitrary({:?})"#,
12✔
791
                        // TODO: This is probably not how we want to represent the TokenTree here.
12✔
792
                        other
12✔
793
                            .iter()
12✔
794
                            .map(|o| o.to_string())
38✔
795
                            .collect::<Vec<String>>()
12✔
796
                            .join(", ")
12✔
797
                    )
798
                }
799
            }
800
        })
12✔
801
        .collect::<Vec<String>>()
186✔
802
        .join(",")
186✔
803
}
186✔
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