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

facet-rs / facet / 14709411237

28 Apr 2025 01:47PM UTC coverage: 57.844%. First build
14709411237

push

github

fasterthanlime
Used parsed enums

156 of 253 new or added lines in 4 files covered. (61.66%)

7706 of 13322 relevant lines covered (57.84%)

77.43 hits per line

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

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

3
/// Processes an enum to implement Facet
4
pub(crate) fn process_enum(parsed: Enum) -> TokenStream {
70✔
5
    let enum_name = parsed.name.to_string();
70✔
6
    let bgp = BoundedGenericParams::parse(parsed.generics.as_ref());
70✔
7
    let where_clauses = build_where_clauses(parsed.clauses.as_ref(), parsed.generics.as_ref());
70✔
8
    let type_params = build_type_params(parsed.generics.as_ref());
70✔
9
    let container_attributes = build_container_attributes(&parsed.attributes);
70✔
10

70✔
11
    // collect all `#repr(..)` attrs
70✔
12
    // either multiple attrs, or a single attr with multiple values
70✔
13
    let attr_iter = parsed
70✔
14
        .attributes
70✔
15
        .iter()
70✔
16
        .filter_map(|attr| {
122✔
17
            if let AttributeInner::Repr(repr_attr) = &attr.body.content {
122✔
18
                if repr_attr.attr.content.0.is_empty() {
70✔
19
                    // treat empty repr as non-existent
20
                    // (this shouldn't be possible, but just in case)
NEW
21
                    None
×
22
                } else {
23
                    Some(repr_attr)
70✔
24
                }
25
            } else {
26
                None
52✔
27
            }
28
        })
122✔
29
        .flat_map(|repr_attr| repr_attr.attr.content.0.iter());
70✔
30

31
    let mut repr_c = false;
70✔
32
    let mut discriminant_type = None;
70✔
33

34
    for attr in attr_iter {
142✔
35
        let attr = attr.value.to_string();
72✔
36
        match attr.as_str() {
72✔
37
            // this is #[repr(C)]
72✔
38
            "C" => repr_c = true,
72✔
39

40
            // set the repr type
41
            // NOTE: we're not worried about multiple
42
            // clashing types here -- that's rustc's problem
43
            "u8" => discriminant_type = Some(Discriminant::U8),
56✔
44
            "u16" => discriminant_type = Some(Discriminant::U16),
7✔
45
            "u32" => discriminant_type = Some(Discriminant::U32),
3✔
46
            "u64" => discriminant_type = Some(Discriminant::U64),
1✔
47
            "usize" => discriminant_type = Some(Discriminant::USize),
1✔
48
            "i8" => discriminant_type = Some(Discriminant::I8),
1✔
49
            "i16" => discriminant_type = Some(Discriminant::I16),
1✔
NEW
50
            "i32" => discriminant_type = Some(Discriminant::I32),
×
NEW
51
            "i64" => discriminant_type = Some(Discriminant::I64),
×
NEW
52
            "isize" => discriminant_type = Some(Discriminant::ISize),
×
53
            _ => {
NEW
54
                return r#"compile_error!("Facet only supports enums with a primitive representation (e.g. #[repr(u8)]) or C-style (e.g. #[repr(C)]")"#
×
NEW
55
            .into_token_stream()
×
56
            }
57
        }
58
    }
59

60
    let params = EnumParams {
70✔
61
        enum_name: &enum_name,
70✔
62
        variants: &parsed.body.content.0,
70✔
63
        discriminant_type,
70✔
64
        bgp: &bgp,
70✔
65
        where_clauses: &where_clauses,
70✔
66
        rename_rule: container_attributes.rename_rule,
70✔
67
    };
70✔
68

69
    let processed_body = match (repr_c, discriminant_type) {
70✔
70
        (true, _) => {
71
            // C-style enum, no discriminant type
72
            process_c_style_enum(&params)
16✔
73
        }
74
        (false, Some(_)) => process_primitive_enum(&params),
54✔
75
        _ => {
NEW
76
            return r#"compile_error!("Enums must have an explicit representation (e.g. #[repr(u8)] or #[repr(C)]) to be used with Facet")"#
×
NEW
77
            .into_token_stream()
×
78
        }
79
    };
80

81
    let ProcessedEnumBody {
82
        shadow_struct_defs,
70✔
83
        variant_expressions,
70✔
84
        repr_type,
70✔
85
    } = processed_body;
70✔
86

70✔
87
    // Join the shadow struct definitions and variant expressions
70✔
88
    let shadow_structs = shadow_struct_defs.join("\n\n");
70✔
89
    let variants = variant_expressions.join(", ");
70✔
90

91
    let static_decl = if parsed.generics.is_none() {
70✔
92
        generate_static_decl(&enum_name)
58✔
93
    } else {
94
        String::new()
12✔
95
    };
96
    let maybe_container_doc = build_maybe_doc(&parsed.attributes);
70✔
97

70✔
98
    // Generate the impl
70✔
99
    let output = format!(
70✔
100
        r#"
70✔
101
{static_decl}
70✔
102

70✔
103
#[automatically_derived]
70✔
104
unsafe impl{bgp_def} ::facet::Facet<'__facet> for {enum_name}{bgp_without_bounds} {where_clauses} {{
70✔
105
    const SHAPE: &'static ::facet::Shape = &const {{
70✔
106
        // Define all shadow structs at the beginning of the const block
70✔
107
        // to ensure they're in scope for offset_of! macros
70✔
108
        {shadow_structs}
70✔
109

70✔
110
        let __facet_variants: &'static [::facet::Variant] = &const {{[
70✔
111
            {variants}
70✔
112
        ]}};
70✔
113

70✔
114
        ::facet::Shape::builder()
70✔
115
            .id(::facet::ConstTypeId::of::<Self>())
70✔
116
            .layout(::core::alloc::Layout::new::<Self>())
70✔
117
            {type_params}
70✔
118
            .vtable(&const {{ ::facet::value_vtable!(
70✔
119
                Self,
70✔
120
                |f, _opts| ::core::fmt::Write::write_str(f, "{enum_name}")
70✔
121
            )}})
70✔
122
            .def(::facet::Def::Enum(::facet::EnumDef::builder()
70✔
123
                // Use variant expressions that just reference the shadow structs
70✔
124
                // which are now defined above
70✔
125
                .variants(__facet_variants)
70✔
126
                .repr(::facet::EnumRepr::{repr_type})
70✔
127
                .build()))
70✔
128
            {maybe_container_doc}
70✔
129
            {container_attributes}
70✔
130
            .build()
70✔
131
    }};
70✔
132
}}
70✔
133
        "#,
70✔
134
        bgp_def = bgp.with_lifetime("__facet").display_with_bounds(),
70✔
135
        bgp_without_bounds = bgp.display_without_bounds(),
70✔
136
        container_attributes = container_attributes.code,
70✔
137
    );
70✔
138

70✔
139
    // Uncomment to see generated code before lexin
70✔
140
    // panic!("output =\n{output}");
70✔
141

70✔
142
    // Return the generated code
70✔
143
    output.into_token_stream()
70✔
144
}
70✔
145

146
/// Build a variant name and attributes, applying rename attribute or rename_all rule
147
fn build_variant_attributes(
186✔
148
    variant_name: &str,
186✔
149
    attributes: &[Attribute],
186✔
150
    rename_rule: Option<RenameRule>,
186✔
151
) -> ContainerAttributes {
186✔
152
    let mut has_explicit_rename = false;
186✔
153
    let mut display_name = variant_name.to_string();
186✔
154
    let mut attribute_list: Vec<String> = vec![];
186✔
155
    let mut rename_all_rule: Option<RenameRule> = None;
186✔
156
    for attr in attributes {
230✔
157
        if let AttributeInner::Facet(facet_attr) = &attr.body.content {
44✔
158
            match &facet_attr.inner.content {
12✔
159
                FacetInner::Sensitive(_) => {
×
160
                    // TODO
×
161
                }
×
162
                FacetInner::Invariants(_) => {
×
163
                    // dealt with elsewhere
×
164
                }
×
165
                FacetInner::Opaque(_) => {
×
166
                    // TODO
×
167
                }
×
168
                FacetInner::DenyUnknownFields(_) => {
×
169
                    // not applicable to variants
×
170
                }
×
171
                FacetInner::DefaultEquals(_) | FacetInner::Default(_) => {
×
172
                    // not applicable to variants
×
173
                }
×
174
                FacetInner::Transparent(_) => {
×
175
                    // not applicable to variants
×
176
                }
×
177
                FacetInner::RenameAll(rename_all_inner) => {
×
178
                    let rule_str = rename_all_inner.value.value().trim_matches('"');
×
179
                    if let Some(rule) = RenameRule::from_str(rule_str) {
×
180
                        rename_all_rule = Some(rule);
×
181
                        attribute_list.push(format!(
×
182
                            r#"::facet::VariantAttribute::RenameAll({:?})"#,
×
183
                            rule_str
×
184
                        ));
×
NEW
185
                    } else {
×
NEW
186
                        panic!("Unknown rename_all rule for enum variant: {:?}", rule_str);
×
187
                    }
188
                }
189
                FacetInner::Other(tt) => {
12✔
190
                    let attr_str = tt.tokens_to_string();
12✔
191

12✔
192
                    // Split the attributes by commas to handle multiple attributes
12✔
193
                    let attrs = attr_str.split(',').map(|s| s.trim()).collect::<Vec<_>>();
13✔
194

195
                    for attr in attrs {
25✔
196
                        if let Some(equal_pos) = attr.find('=') {
13✔
197
                            let key = attr[..equal_pos].trim();
12✔
198
                            if key == "rename" {
12✔
199
                                has_explicit_rename = true;
9✔
200
                                let value = attr[equal_pos + 1..].trim().trim_matches('"');
9✔
201
                                // Keep the Rename attribute for reflection
9✔
202
                                attribute_list.push(format!(
9✔
203
                                    r#"::facet::VariantAttribute::Rename({:?})"#,
9✔
204
                                    value
9✔
205
                                ));
9✔
206
                                display_name = value.to_string();
9✔
207
                            } else if key == "rename_all" {
9✔
208
                                let rule_str = attr[equal_pos + 1..].trim().trim_matches('"');
×
209
                                if let Some(rule) = RenameRule::from_str(rule_str) {
×
210
                                    rename_all_rule = Some(rule);
×
211
                                    attribute_list.push(format!(
×
212
                                        r#"::facet::VariantAttribute::RenameAll({:?})"#,
×
213
                                        rule_str
×
214
                                    ));
×
215
                                }
×
216
                            } else {
3✔
217
                                attribute_list.push(format!(
3✔
218
                                    r#"::facet::VariantAttribute::Arbitrary({:?})"#,
3✔
219
                                    attr
3✔
220
                                ));
3✔
221
                            }
3✔
222
                        } else {
1✔
223
                            attribute_list.push(format!(
1✔
224
                                r#"::facet::VariantAttribute::Arbitrary({:?})"#,
1✔
225
                                attr
1✔
226
                            ));
1✔
227
                        }
1✔
228
                    }
229
                }
230
            }
231
        }
32✔
232
    }
233

234
    if !has_explicit_rename && rename_rule.is_some() {
186✔
235
        display_name = rename_rule.unwrap().apply(variant_name);
7✔
236
    }
179✔
237

238
    let attributes_string = if attribute_list.is_empty() {
186✔
239
        format!(".name({:?})", display_name)
174✔
240
    } else {
241
        format!(
12✔
242
            ".name({:?}).attributes(&[{}])",
12✔
243
            display_name,
12✔
244
            attribute_list.join(", ")
12✔
245
        )
246
    };
247

248
    ContainerAttributes {
186✔
249
        code: attributes_string,
186✔
250
        rename_rule: rename_all_rule,
186✔
251
    }
186✔
252
}
186✔
253

254
// mirrors facet_core::types::EnumRepr
255
#[derive(Clone, Copy)]
256
enum Discriminant {
257
    U8,
258
    U16,
259
    U32,
260
    U64,
261
    USize,
262
    I8,
263
    I16,
264
    I32,
265
    I64,
266
    ISize,
267
}
268

269
impl Discriminant {
270
    fn as_enum_repr(&self) -> &'static str {
56✔
271
        match self {
56✔
272
            Discriminant::U8 => "U8",
49✔
273
            Discriminant::U16 => "U16",
4✔
274
            Discriminant::U32 => "U32",
2✔
275
            Discriminant::U64 => "U64",
×
276
            Discriminant::USize => "USize",
×
277
            Discriminant::I8 => "I8",
×
278
            Discriminant::I16 => "I16",
1✔
279
            Discriminant::I32 => "I32",
×
280
            Discriminant::I64 => "I64",
×
281
            Discriminant::ISize => "ISize",
×
282
        }
283
    }
56✔
284

285
    fn as_rust_type(&self) -> &'static str {
80✔
286
        match self {
80✔
287
            Discriminant::U8 => "u8",
68✔
288
            Discriminant::U16 => "u16",
9✔
289
            Discriminant::U32 => "u32",
2✔
290
            Discriminant::U64 => "u64",
×
291
            Discriminant::USize => "usize",
×
292
            Discriminant::I8 => "i8",
×
293
            Discriminant::I16 => "i16",
1✔
294
            Discriminant::I32 => "i32",
×
295
            Discriminant::I64 => "i64",
×
296
            Discriminant::ISize => "isize",
×
297
        }
298
    }
80✔
299
}
300

301
struct ProcessedEnumBody {
302
    shadow_struct_defs: Vec<String>,
303
    variant_expressions: Vec<String>,
304
    repr_type: String,
305
}
306

307
type EnumVariant = Delimited<EnumVariantLike, Comma>;
308

309
struct EnumParams<'a> {
310
    // Core identification
311
    enum_name: &'a str,
312
    variants: &'a [EnumVariant],
313

314
    // Type information
315
    discriminant_type: Option<Discriminant>,
316
    bgp: &'a BoundedGenericParams,
317
    where_clauses: &'a str,
318

319
    // Attributes and customization
320
    rename_rule: Option<RenameRule>,
321
}
322

323
impl EnumParams<'_> {
324
    fn with_facet_lifetime(&self) -> BoundedGenericParams {
70✔
325
        self.bgp.with(BoundedGenericParam {
70✔
326
            bounds: None,
70✔
327
            param: GenericParamName::Lifetime("__facet".into()),
70✔
328
        })
70✔
329
    }
70✔
330
}
331

332
/// C-style enums (i.e. #[repr(C)], #[repr(C, u*)] and #[repr(C, i*)]) are laid out
333
/// as a #[repr(C)] struct with two fields: the discriminant and the union of all the variants.
334
///
335
/// See: <https://doc.rust-lang.org/reference/type-layout.html#r-layout.repr.primitive.adt>
336
///
337
/// To calculate the offsets of each variant, we create a shadow struct that mimics this
338
/// structure and use the `offset_of!` macro to calculate the offsets of each field.
339
fn process_c_style_enum(params: &EnumParams) -> ProcessedEnumBody {
16✔
340
    let facet_bgp = params.with_facet_lifetime();
16✔
341

16✔
342
    // Collect shadow struct definitions separately from variant expressions
16✔
343
    let mut shadow_struct_defs = Vec::new();
16✔
344
    let mut variant_expressions = Vec::new();
16✔
345

16✔
346
    // first, create an enum to represent the discriminant type
16✔
347
    let shadow_discriminant_name = format!("__ShadowDiscriminant{}", params.enum_name);
16✔
348
    let all_variant_names = params
16✔
349
        .variants
16✔
350
        .iter()
16✔
351
        .map(|var_like| match &var_like.value.variant {
44✔
352
            EnumVariantData::Unit(unit) => unit.name.to_string(),
10✔
353
            EnumVariantData::Tuple(tuple) => tuple.name.to_string(),
21✔
354
            EnumVariantData::Struct(struct_var) => struct_var.name.to_string(),
13✔
355
        })
44✔
356
        .collect::<Vec<_>>()
16✔
357
        .join(", ");
16✔
358
    shadow_struct_defs.push(format!(
16✔
359
        "#[repr({repr})] enum {shadow_discriminant_name} {{ {all_variant_names} }}",
16✔
360
        // repr is either C or the explicit discriminant type
16✔
361
        repr = params
16✔
362
            .discriminant_type
16✔
363
            .map(|d| d.as_rust_type())
16✔
364
            .unwrap_or("C")
16✔
365
    ));
16✔
366

16✔
367
    // we'll also generate a shadow union for the fields
16✔
368
    let shadow_union_name = format!("__ShadowFields{}", params.enum_name);
16✔
369

16✔
370
    let all_union_fields = params
16✔
371
        .variants
16✔
372
        .iter()
16✔
373
        .map(|var_like| match &var_like.value.variant {
44✔
374
            EnumVariantData::Unit(unit) => unit.name.to_string(),
10✔
375
            EnumVariantData::Tuple(tuple) => tuple.name.to_string(),
21✔
376
            EnumVariantData::Struct(struct_var) => struct_var.name.to_string(),
13✔
377
        })
44✔
378
        .map(|variant_name| {
44✔
379
            format!(
44✔
380
                "{variant_name}: std::mem::ManuallyDrop<__ShadowField{}_{variant_name}{bgp}>",
44✔
381
                params.enum_name,
44✔
382
                bgp = facet_bgp.display_without_bounds()
44✔
383
            )
44✔
384
        })
44✔
385
        .collect::<Vec<_>>()
16✔
386
        .join(", ");
16✔
387

16✔
388
    shadow_struct_defs.push(format!(
16✔
389
        "#[repr(C)] union {shadow_union_name}{bgp} {} {{ {all_union_fields} }}",
16✔
390
        params.where_clauses,
16✔
391
        bgp = facet_bgp.display_with_bounds()
16✔
392
    ));
16✔
393

16✔
394
    // Create a shadow struct to represent the enum layout
16✔
395
    let shadow_repr_name = format!("__ShadowRepr{}", params.enum_name);
16✔
396

16✔
397
    shadow_struct_defs.push(format!(
16✔
398
        "#[repr(C)] struct {shadow_repr_name}{struct_bgp} {} {{
16✔
399
            _discriminant: {shadow_discriminant_name},
16✔
400
            _phantom: {phantom},
16✔
401
            _fields: {shadow_union_name}{fields_bgp},
16✔
402
        }}",
16✔
403
        params.where_clauses,
16✔
404
        struct_bgp = facet_bgp.display_with_bounds(),
16✔
405
        fields_bgp = facet_bgp.display_without_bounds(),
16✔
406
        phantom = facet_bgp.display_as_phantom_data(),
16✔
407
    ));
16✔
408

16✔
409
    // Discriminant values are either manually defined, or incremented from the last one
16✔
410
    // See: <https://doc.rust-lang.org/reference/items/enumerations.html#implicit-discriminants>
16✔
411
    let mut discriminant_value = 0;
16✔
412
    for var_like in params.variants.iter() {
44✔
413
        if let Some(x) = &var_like.value.discriminant {
44✔
414
            discriminant_value = get_discriminant_value(&x.second);
×
415
        }
44✔
416

417
        match &var_like.value.variant {
44✔
418
            EnumVariantData::Unit(unit) => {
10✔
419
                let variant_name = unit.name.to_string();
10✔
420
                let container_attributes =
10✔
421
                    build_variant_attributes(&variant_name, &unit.attributes, params.rename_rule);
10✔
422
                let maybe_doc = build_maybe_doc(&unit.attributes);
10✔
423

10✔
424
                // Generate shadow struct for this tuple variant to calculate offsets
10✔
425
                let shadow_struct_name =
10✔
426
                    format!("__ShadowField{}_{variant_name}", params.enum_name);
10✔
427

10✔
428
                // Add shadow struct definition
10✔
429
                shadow_struct_defs.push(format!(
10✔
430
                    "#[repr(C)] struct {shadow_struct_name}{bgp} {} {{ _phantom: {phantom} }}",
10✔
431
                    params.where_clauses,
10✔
432
                    bgp = facet_bgp.display_with_bounds(),
10✔
433
                    phantom = facet_bgp.display_as_phantom_data(),
10✔
434
                ));
10✔
435

10✔
436
                // variant offset is offset of the `_fields` union
10✔
437
                variant_expressions.push(format!(
10✔
438
                    "::facet::Variant::builder()
10✔
439
                    {container_attributes}
10✔
440
                    .discriminant({discriminant_value})
10✔
441
                    .fields(::facet::StructDef::builder().unit().build())
10✔
442
                    {maybe_doc}
10✔
443
                    .build()",
10✔
444
                    container_attributes = container_attributes.code
10✔
445
                ));
10✔
446
            }
10✔
447
            EnumVariantData::Tuple(tuple) => {
21✔
448
                let variant_name = tuple.name.to_string();
21✔
449
                let container_attributes =
21✔
450
                    build_variant_attributes(&variant_name, &tuple.attributes, params.rename_rule);
21✔
451
                let maybe_doc = build_maybe_doc(&tuple.attributes);
21✔
452

21✔
453
                // Generate shadow struct for this tuple variant to calculate offsets
21✔
454
                let shadow_struct_name =
21✔
455
                    format!("__ShadowField{}_{variant_name}", params.enum_name);
21✔
456

21✔
457
                // Build the list of fields and types for the shadow struct
21✔
458
                let fields_with_types = tuple
21✔
459
                    .fields
21✔
460
                    .content
21✔
461
                    .0
21✔
462
                    .iter()
21✔
463
                    .enumerate()
21✔
464
                    .map(|(idx, field)| {
32✔
465
                        let typ = VerbatimDisplay(&field.value.typ).to_string();
32✔
466
                        format!("_{}: {}", idx, typ)
32✔
467
                    })
32✔
468
                    .collect::<Vec<String>>()
21✔
469
                    .join(", ");
21✔
470

21✔
471
                // Add shadow struct definition
21✔
472
                shadow_struct_defs.push(format!(
21✔
473
                    "#[repr(C)] struct {shadow_struct_name}{bgp} {} {{ {fields_with_types}, _phantom: {phantom} }}",
21✔
474
                    params.where_clauses,
21✔
475
                    bgp = facet_bgp.display_with_bounds(),
21✔
476
                    phantom = facet_bgp.display_as_phantom_data(),
21✔
477
                ));
21✔
478

21✔
479
                let variant_offset = format!(
21✔
480
                    "::core::mem::offset_of!({shadow_repr_name}{facet_bgp_use}, _fields)",
21✔
481
                    facet_bgp_use = facet_bgp.display_without_bounds()
21✔
482
                );
21✔
483

21✔
484
                // Build the list of field types with calculated offsets
21✔
485
                let fields = tuple
21✔
486
                    .fields
21✔
487
                    .content
21✔
488
                    .0
21✔
489
                    .iter()
21✔
490
                    .enumerate()
21✔
491
                    .map(|(idx, field)| {
32✔
492
                        let field_name = format!("_{idx}");
32✔
493
                        gen_struct_field(FieldInfo {
32✔
494
                            raw_field_name: &field_name,
32✔
495
                            normalized_field_name: &field_name,
32✔
496
                            field_type: &field.value.typ.tokens_to_string(),
32✔
497
                            struct_name: &shadow_struct_name,
32✔
498
                            bgp: &facet_bgp,
32✔
499
                            attrs: &field.value.attributes,
32✔
500
                            base_field_offset: Some(&variant_offset),
32✔
501
                            rename_rule: container_attributes.rename_rule,
32✔
502
                        })
32✔
503
                    })
32✔
504
                    .collect::<Vec<String>>()
21✔
505
                    .join(", ");
21✔
506

21✔
507
                // Add variant expression - now with discriminant
21✔
508
                variant_expressions.push(format!(
21✔
509
                    "{{
21✔
510
                        let fields: &'static [::facet::Field] = &const {{[
21✔
511
                            {fields}
21✔
512
                        ]}};
21✔
513

21✔
514
                        ::facet::Variant::builder()
21✔
515
                            {container_attributes}
21✔
516
                            .discriminant({discriminant_value})
21✔
517
                            .fields(::facet::StructDef::builder().tuple().fields(fields).build())
21✔
518
                            {maybe_doc}
21✔
519
                            .build()
21✔
520
                    }}",
21✔
521
                    container_attributes = container_attributes.code
522
                ));
523
            }
524
            EnumVariantData::Struct(struct_var) => {
13✔
525
                let variant_name = struct_var.name.to_string();
13✔
526
                let container_attributes = build_variant_attributes(
13✔
527
                    &variant_name,
13✔
528
                    &struct_var.attributes,
13✔
529
                    params.rename_rule,
13✔
530
                );
13✔
531
                let maybe_doc = build_maybe_doc(&struct_var.attributes);
13✔
532

13✔
533
                // Generate shadow struct for this struct variant to calculate offsets
13✔
534
                let shadow_struct_name =
13✔
535
                    format!("__ShadowField{}_{variant_name}", params.enum_name);
13✔
536

13✔
537
                // Build the list of fields and types
13✔
538
                let fields_with_types = struct_var
13✔
539
                    .fields
13✔
540
                    .content
13✔
541
                    .0
13✔
542
                    .iter()
13✔
543
                    .map(|field| {
27✔
544
                        let name = field.value.name.to_string();
27✔
545
                        let typ = VerbatimDisplay(&field.value.typ).to_string();
27✔
546
                        format!("{}: {}", name, typ)
27✔
547
                    })
27✔
548
                    .collect::<Vec<String>>()
13✔
549
                    .join(", ");
13✔
550

13✔
551
                // Add shadow struct definition
13✔
552
                shadow_struct_defs.push(format!(
13✔
553
                    "#[repr(C)] struct {shadow_struct_name}{bgp} {} {{ {fields_with_types}, _phantom: {phantom} }}",
13✔
554
                    params.where_clauses,
13✔
555
                    bgp = facet_bgp.display_with_bounds(),
13✔
556
                    phantom = facet_bgp.display_as_phantom_data(),
13✔
557
                ));
13✔
558

13✔
559
                let variant_offset = format!(
13✔
560
                    "::core::mem::offset_of!({shadow_repr_name}{facet_bgp_use}, _fields)",
13✔
561
                    facet_bgp_use = facet_bgp.display_without_bounds()
13✔
562
                );
13✔
563

13✔
564
                // Build the list of field types with calculated offsets
13✔
565
                let fields = struct_var
13✔
566
                    .fields
13✔
567
                    .content
13✔
568
                    .0
13✔
569
                    .iter()
13✔
570
                    .map(|field| {
27✔
571
                        // Handle raw identifiers (like r#type) by stripping the 'r#' prefix.
27✔
572
                        let raw_field_name = field.value.name.to_string(); // e.g., "r#type"
27✔
573
                        let normalized_field_name = normalize_ident_str(&raw_field_name); // e.g., "type"
27✔
574
                        let field_type = field.value.typ.tokens_to_string();
27✔
575
                        gen_struct_field(FieldInfo {
27✔
576
                            raw_field_name: &raw_field_name,
27✔
577
                            normalized_field_name,
27✔
578
                            field_type: &field_type,
27✔
579
                            struct_name: &shadow_struct_name,
27✔
580
                            bgp: &facet_bgp,
27✔
581
                            attrs: &field.value.attributes,
27✔
582
                            base_field_offset: Some(&variant_offset),
27✔
583
                            rename_rule: container_attributes.rename_rule,
27✔
584
                        })
27✔
585
                    })
27✔
586
                    .collect::<Vec<String>>()
13✔
587
                    .join(", ");
13✔
588

13✔
589
                // Add variant expression - now with discriminant
13✔
590
                variant_expressions.push(format!(
13✔
591
                    "{{
13✔
592
                        let fields: &'static [::facet::Field] = &const {{[
13✔
593
                            {fields}
13✔
594
                        ]}};
13✔
595

13✔
596
                        ::facet::Variant::builder()
13✔
597
                            {container_attributes}
13✔
598
                            .discriminant({discriminant_value})
13✔
599
                            .fields(::facet::StructDef::builder().struct_().fields(fields).build())
13✔
600
                            {maybe_doc}
13✔
601
                            .build()
13✔
602
                    }}",
13✔
603
                    container_attributes = container_attributes.code
604
                ));
605
            }
606
        }
607
        discriminant_value += 1;
44✔
608
    }
609

610
    ProcessedEnumBody {
611
        shadow_struct_defs,
16✔
612
        variant_expressions,
16✔
613
        repr_type: params.discriminant_type.map_or_else(
16✔
614
            || format!("from_discriminant_size::<{shadow_discriminant_name}>()"),
14✔
615
            |d| d.as_enum_repr().to_string(),
2✔
616
        ),
617
    }
618
}
16✔
619

620
/// Primitive enums (i.e. #[repr(u*)] and #[repr(i*)]) are laid out
621
/// as a union of all the variants, with the discriminant as an "inner" tag in the struct.
622
///
623
/// See: <https://doc.rust-lang.org/reference/type-layout.html#r-layout.repr.primitive.adt>
624
///
625
/// To calculate the offsets of each variant, we create a shadow struct that mimics this
626
/// structure and use the `offset_of!` macro to calculate the offsets of each field.
627
fn process_primitive_enum(params: &EnumParams) -> ProcessedEnumBody {
54✔
628
    let facet_bgp = params.with_facet_lifetime();
54✔
629

54✔
630
    // Collect shadow struct definitions separately from variant expressions
54✔
631
    let mut shadow_struct_defs = Vec::new();
54✔
632
    let mut variant_expressions = Vec::new();
54✔
633

54✔
634
    // We can safely unwrap because this function is only called when discriminant_type is Some
54✔
635
    let discriminant_type = params
54✔
636
        .discriminant_type
54✔
637
        .expect("discriminant_type should be Some when process_primitive_enum is called");
54✔
638

54✔
639
    // Discriminant values are either manually defined, or incremented from the last one
54✔
640
    // See: <https://doc.rust-lang.org/reference/items/enumerations.html#implicit-discriminants>
54✔
641
    let mut discriminant_value = 0;
54✔
642
    for var_like in params.variants.iter() {
142✔
643
        if let Some(x) = &var_like.value.discriminant {
142✔
644
            discriminant_value = get_discriminant_value(&x.second);
17✔
645
        }
125✔
646
        match &var_like.value.variant {
142✔
647
            EnumVariantData::Unit(unit) => {
64✔
648
                let variant_name = unit.name.to_string();
64✔
649
                let container_attributes =
64✔
650
                    build_variant_attributes(&variant_name, &unit.attributes, params.rename_rule);
64✔
651
                let maybe_doc = build_maybe_doc(&unit.attributes);
64✔
652

64✔
653
                variant_expressions.push(format!(
64✔
654
                    "::facet::Variant::builder()
64✔
655
                    {container_attributes}
64✔
656
                    .discriminant({discriminant_value})
64✔
657
                    .fields(::facet::StructDef::builder().unit().build())
64✔
658
                    {maybe_doc}
64✔
659
                    .build()",
64✔
660
                    container_attributes = container_attributes.code
64✔
661
                ));
64✔
662
            }
64✔
663
            EnumVariantData::Tuple(tuple) => {
44✔
664
                let variant_name = tuple.name.to_string();
44✔
665
                let container_attributes =
44✔
666
                    build_variant_attributes(&variant_name, &tuple.attributes, params.rename_rule);
44✔
667
                let maybe_doc = build_maybe_doc(&tuple.attributes);
44✔
668

44✔
669
                // Generate shadow struct for this tuple variant to calculate offsets
44✔
670
                let shadow_struct_name = format!("__Shadow{}_{}", params.enum_name, variant_name);
44✔
671

44✔
672
                // Build the list of fields and types for the shadow struct
44✔
673
                let fields_with_types = tuple
44✔
674
                    .fields
44✔
675
                    .content
44✔
676
                    .0
44✔
677
                    .iter()
44✔
678
                    .enumerate()
44✔
679
                    .map(|(idx, field)| {
58✔
680
                        let typ = VerbatimDisplay(&field.value.typ).to_string();
58✔
681
                        format!("_{}: {}", idx, typ)
58✔
682
                    })
58✔
683
                    .collect::<Vec<String>>()
44✔
684
                    .join(", ");
44✔
685

44✔
686
                // Add shadow struct definition
44✔
687
                shadow_struct_defs.push(format!(
44✔
688
                    "#[repr(C)] struct {shadow_struct_name}{bgp} {}  {{
44✔
689
                        _discriminant: {discriminant},
44✔
690
                        _phantom: {phantom},
44✔
691
                        {fields_with_types}
44✔
692
                    }}",
44✔
693
                    params.where_clauses,
44✔
694
                    bgp = facet_bgp.display_with_bounds(),
44✔
695
                    phantom = facet_bgp.display_as_phantom_data(),
44✔
696
                    discriminant = discriminant_type.as_rust_type(),
44✔
697
                ));
44✔
698

44✔
699
                // Build the list of field types with calculated offsets
44✔
700
                let fields = tuple
44✔
701
                    .fields
44✔
702
                    .content
44✔
703
                    .0
44✔
704
                    .iter()
44✔
705
                    .enumerate()
44✔
706
                    .map(|(idx, field)| {
58✔
707
                        let field_name = format!("_{idx}");
58✔
708
                        gen_struct_field(FieldInfo {
58✔
709
                            raw_field_name: &field_name,
58✔
710
                            normalized_field_name: &field_name,
58✔
711
                            field_type: &field.value.typ.tokens_to_string(),
58✔
712
                            struct_name: &shadow_struct_name,
58✔
713
                            bgp: &facet_bgp,
58✔
714
                            attrs: &field.value.attributes,
58✔
715
                            base_field_offset: None,
58✔
716
                            rename_rule: container_attributes.rename_rule,
58✔
717
                        })
58✔
718
                    })
58✔
719
                    .collect::<Vec<String>>()
44✔
720
                    .join(", ");
44✔
721

44✔
722
                // Add variant expression - now with discriminant
44✔
723
                variant_expressions.push(format!(
44✔
724
                    "{{
44✔
725
                        let fields: &'static [::facet::Field] = &const {{[
44✔
726
                            {fields}
44✔
727
                        ]}};
44✔
728

44✔
729
                        ::facet::Variant::builder()
44✔
730
                            {container_attributes}
44✔
731
                            .discriminant({discriminant_value})
44✔
732
                            .fields(::facet::StructDef::builder().tuple().fields(fields).build())
44✔
733
                            {maybe_doc}
44✔
734
                            .build()
44✔
735
                    }}",
44✔
736
                    container_attributes = container_attributes.code
737
                ));
738
            }
739
            EnumVariantData::Struct(struct_var) => {
34✔
740
                let variant_name = struct_var.name.to_string();
34✔
741
                let container_attributes = build_variant_attributes(
34✔
742
                    &variant_name,
34✔
743
                    &struct_var.attributes,
34✔
744
                    params.rename_rule,
34✔
745
                );
34✔
746
                let maybe_doc = build_maybe_doc(&struct_var.attributes);
34✔
747

34✔
748
                // Generate shadow struct for this struct variant to calculate offsets
34✔
749
                let shadow_struct_name = format!("__Shadow{}_{}", params.enum_name, variant_name);
34✔
750

34✔
751
                // Build the list of fields and types
34✔
752
                let fields_with_types = struct_var
34✔
753
                    .fields
34✔
754
                    .content
34✔
755
                    .0
34✔
756
                    .iter()
34✔
757
                    .map(|field| {
60✔
758
                        let name = field.value.name.to_string();
60✔
759
                        let typ = VerbatimDisplay(&field.value.typ).to_string();
60✔
760
                        format!("{}: {}", name, typ)
60✔
761
                    })
60✔
762
                    .collect::<Vec<String>>()
34✔
763
                    .join(", ");
34✔
764

34✔
765
                // Add shadow struct definition
34✔
766
                shadow_struct_defs.push(format!(
34✔
767
                    "#[repr(C)] struct {shadow_struct_name}{bgp} {} {{
34✔
768
                        _discriminant: {discriminant},
34✔
769
                        _phantom: {phantom},
34✔
770
                        {fields_with_types}
34✔
771
                    }}",
34✔
772
                    params.where_clauses,
34✔
773
                    bgp = facet_bgp.display_with_bounds(),
34✔
774
                    phantom = facet_bgp.display_as_phantom_data(),
34✔
775
                    discriminant = discriminant_type.as_rust_type(),
34✔
776
                ));
34✔
777

34✔
778
                // Build the list of field types with calculated offsets
34✔
779
                let fields = struct_var
34✔
780
                    .fields
34✔
781
                    .content
34✔
782
                    .0
34✔
783
                    .iter()
34✔
784
                    .map(|field| {
60✔
785
                        // Handle raw identifiers (like r#type) by stripping the 'r#' prefix.
60✔
786
                        let raw_field_name = field.value.name.to_string(); // e.g., "r#type"
60✔
787
                        let normalized_field_name = normalize_ident_str(&raw_field_name); // e.g., "type"
60✔
788
                        gen_struct_field(FieldInfo {
60✔
789
                            raw_field_name: &raw_field_name,
60✔
790
                            normalized_field_name,
60✔
791
                            field_type: &field.value.typ.tokens_to_string(),
60✔
792
                            struct_name: &shadow_struct_name,
60✔
793
                            bgp: &facet_bgp,
60✔
794
                            attrs: &field.value.attributes,
60✔
795
                            base_field_offset: None,
60✔
796
                            rename_rule: container_attributes.rename_rule,
60✔
797
                        })
60✔
798
                    })
60✔
799
                    .collect::<Vec<String>>()
34✔
800
                    .join(", ");
34✔
801

34✔
802
                // Add variant expression - now with discriminant
34✔
803
                // variant offset is zero since all fields are
34✔
804
                // already computed relative to the discriminant
34✔
805
                variant_expressions.push(format!(
34✔
806
                    "{{
34✔
807
                        let fields: &'static [::facet::Field] = &const {{[
34✔
808
                            {fields}
34✔
809
                        ]}};
34✔
810

34✔
811
                        ::facet::Variant::builder()
34✔
812
                            {container_attributes}
34✔
813
                            .discriminant({discriminant_value})
34✔
814
                            .fields(::facet::StructDef::builder().struct_().fields(fields).build())
34✔
815
                            {maybe_doc}
34✔
816
                            .build()
34✔
817
                    }}",
34✔
818
                    container_attributes = container_attributes.code
819
                ));
820
            }
821
        }
822
        discriminant_value += 1;
142✔
823
    }
824

825
    ProcessedEnumBody {
54✔
826
        shadow_struct_defs,
54✔
827
        variant_expressions,
54✔
828
        repr_type: discriminant_type.as_enum_repr().to_string(),
54✔
829
    }
54✔
830
}
54✔
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