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

facet-rs / facet / 19898374562

03 Dec 2025 03:01PM UTC coverage: 58.881% (-0.02%) from 58.902%
19898374562

Pull #1005

github

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

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

220 existing lines in 6 files now uncovered.

20587 of 34964 relevant lines covered (58.88%)

539.09 hits per line

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

98.25
/facet-macros-impl/src/process_struct.rs
1
use quote::{format_ident, quote};
2

3
use super::*;
4

5
/// Generates the `::facet::Field` definition `TokenStream` from a `PStructField`.
6
pub(crate) fn gen_field_from_pfield(
2,976✔
7
    field: &PStructField,
2,976✔
8
    struct_name: &Ident,
2,976✔
9
    bgp: &BoundedGenericParams,
2,976✔
10
    base_offset: Option<TokenStream>,
2,976✔
11
    facet_crate: &TokenStream,
2,976✔
12
) -> TokenStream {
2,976✔
13
    let field_name_effective = &field.name.effective;
2,976✔
14
    let field_name_raw = &field.name.raw;
2,976✔
15
    let field_type = &field.ty;
2,976✔
16

17
    let bgp_without_bounds = bgp.display_without_bounds();
2,976✔
18

19
    let doc_lines: Vec<String> = field
2,976✔
20
        .attrs
2,976✔
21
        .doc
2,976✔
22
        .iter()
2,976✔
23
        .map(|doc| doc.as_str().replace("\\\"", "\""))
2,976✔
24
        .collect();
2,976✔
25

26
    // Check for opaque attribute to determine shape_of variant
27
    // (Required at compile time - shape_of requires Facet, shape_of_opaque wraps in Opaque)
28
    let shape_of = if field.attrs.has_builtin("opaque") {
2,976✔
29
        quote! { shape_of_opaque }
29✔
30
    } else {
31
        quote! { shape_of }
2,947✔
32
    };
33

34
    // All attributes go through grammar dispatch
35
    // Note: deserialize_with and serialize_with have been REMOVED from the grammar.
36
    // Use #[facet(proxy = Type)] for custom serialization instead.
37
    let mut attribute_list: Vec<TokenStream> = field
2,976✔
38
        .attrs
2,976✔
39
        .facet
2,976✔
40
        .iter()
2,976✔
41
        .map(|attr| {
2,976✔
42
            let ext_attr = emit_attr_for_field(attr, field_name_raw, field_type, facet_crate);
947✔
43
            quote! { #ext_attr }
947✔
44
        })
947✔
45
        .collect();
2,976✔
46

47
    // Generate proxy conversion function pointers when proxy attribute is present
48
    if let Some(attr) = field
2,976✔
49
        .attrs
2,976✔
50
        .facet
2,976✔
51
        .iter()
2,976✔
52
        .find(|a| a.is_builtin() && a.key_str() == "proxy")
2,976✔
53
    {
31✔
54
        let proxy_type = &attr.args;
31✔
55

31✔
56
        // Generate __proxy_in: converts proxy -> field type via TryFrom
31✔
57
        attribute_list.push(quote! {
31✔
58
            #facet_crate::ExtensionAttr {
31✔
59
                ns: ::core::option::Option::None,
31✔
60
                key: "__proxy_in",
31✔
61
                data: &const {
31✔
62
                    extern crate alloc as __alloc;
31✔
63
                    unsafe fn __proxy_convert_in<'mem>(
31✔
64
                        proxy_ptr: #facet_crate::PtrConst<'mem>,
31✔
65
                        field_ptr: #facet_crate::PtrUninit<'mem>,
31✔
66
                    ) -> ::core::result::Result<#facet_crate::PtrMut<'mem>, __alloc::string::String> {
31✔
67
                        let proxy: #proxy_type = proxy_ptr.read();
31✔
68
                        match <#field_type as ::core::convert::TryFrom<#proxy_type>>::try_from(proxy) {
31✔
69
                            ::core::result::Result::Ok(value) => ::core::result::Result::Ok(field_ptr.put(value)),
31✔
70
                            ::core::result::Result::Err(e) => ::core::result::Result::Err(__alloc::string::ToString::to_string(&e)),
31✔
71
                        }
31✔
72
                    }
31✔
73
                    __proxy_convert_in as #facet_crate::ProxyConvertInFn
31✔
74
                } as *const #facet_crate::ProxyConvertInFn as *const (),
31✔
75
                shape: <() as #facet_crate::Facet>::SHAPE,
31✔
76
            }
31✔
77
        });
31✔
78

31✔
79
        // Generate __proxy_out: converts &field type -> proxy via TryFrom
31✔
80
        attribute_list.push(quote! {
31✔
81
            #facet_crate::ExtensionAttr {
31✔
82
                ns: ::core::option::Option::None,
31✔
83
                key: "__proxy_out",
31✔
84
                data: &const {
31✔
85
                    extern crate alloc as __alloc;
31✔
86
                    unsafe fn __proxy_convert_out<'mem>(
31✔
87
                        field_ptr: #facet_crate::PtrConst<'mem>,
31✔
88
                        proxy_ptr: #facet_crate::PtrUninit<'mem>,
31✔
89
                    ) -> ::core::result::Result<#facet_crate::PtrMut<'mem>, __alloc::string::String> {
31✔
90
                        let field_ref: &#field_type = field_ptr.get();
31✔
91
                        match <#proxy_type as ::core::convert::TryFrom<&#field_type>>::try_from(field_ref) {
31✔
92
                            ::core::result::Result::Ok(proxy) => ::core::result::Result::Ok(proxy_ptr.put(proxy)),
31✔
93
                            ::core::result::Result::Err(e) => ::core::result::Result::Err(__alloc::string::ToString::to_string(&e)),
31✔
94
                        }
31✔
95
                    }
31✔
96
                    __proxy_convert_out as #facet_crate::ProxyConvertOutFn
31✔
97
                } as *const #facet_crate::ProxyConvertOutFn as *const (),
31✔
98
                shape: <() as #facet_crate::Facet>::SHAPE,
31✔
99
            }
31✔
100
        });
31✔
101
    }
2,945✔
102

103
    let maybe_attributes = if attribute_list.is_empty() {
2,976✔
104
        quote! {}
2,154✔
105
    } else {
106
        quote! { .attributes(&const { [#(#attribute_list),*] }) }
822✔
107
    };
108

109
    let maybe_field_doc = if doc_lines.is_empty() {
2,976✔
110
        quote! {}
2,922✔
111
    } else {
112
        quote! { .doc(&[#(#doc_lines),*]) }
54✔
113
    };
114

115
    // Calculate the final offset, incorporating the base_offset if present
116
    let final_offset = match base_offset {
2,976✔
117
        Some(base) => {
115✔
118
            quote! { #base + ::core::mem::offset_of!(#struct_name #bgp_without_bounds, #field_name_raw) }
115✔
119
        }
120
        None => {
121
            quote! { ::core::mem::offset_of!(#struct_name #bgp_without_bounds, #field_name_raw) }
2,861✔
122
        }
123
    };
124

125
    quote! {
2,976✔
126
        {
127
            #facet_crate::Field::builder()
128
                // Use the effective name (after rename rules) for metadata
129
                .name(#field_name_effective)
130
                // Use the raw field name/index TokenStream for shape_of and offset_of
131
                .shape(|| #facet_crate::#shape_of(&|s: &#struct_name #bgp_without_bounds| &s.#field_name_raw))
132
                .offset(#final_offset)
133
                #maybe_attributes
134
                #maybe_field_doc
135
                .build()
136
        }
137
    }
138
}
2,976✔
139

140
/// Processes a regular struct to implement Facet
141
///
142
/// Example input:
143
/// ```rust
144
/// struct Blah {
145
///     foo: u32,
146
///     bar: String,
147
/// }
148
/// ```
149
pub(crate) fn process_struct(parsed: Struct) -> TokenStream {
1,510✔
150
    let ps = PStruct::parse(&parsed); // Use the parsed representation
1,510✔
151

152
    let struct_name_ident = format_ident!("{}", ps.container.name);
1,510✔
153
    let struct_name = &ps.container.name;
1,510✔
154
    let struct_name_str = struct_name.to_string();
1,510✔
155

156
    let opaque = ps.container.attrs.has_builtin("opaque");
1,510✔
157

158
    // Get the facet crate path (custom or default ::facet)
159
    let facet_crate = ps.container.attrs.facet_crate();
1,510✔
160

161
    let type_name_fn =
1,510✔
162
        generate_type_name_fn(struct_name, parsed.generics.as_ref(), opaque, &facet_crate);
1,510✔
163

164
    // TODO: I assume the `PrimitiveRepr` is only relevant for enums, and does not need to be preserved?
165
    let repr = match &ps.container.attrs.repr {
1,510✔
166
        PRepr::Transparent => quote! { #facet_crate::Repr::transparent() },
4✔
167
        PRepr::Rust(_) => quote! { #facet_crate::Repr::default() },
1,505✔
168
        PRepr::C(_) => quote! { #facet_crate::Repr::c() },
1✔
169
    };
170

171
    // Use PStruct for kind and fields
172
    let (kind, fields_vec) = match &ps.kind {
1,510✔
173
        PStructKind::Struct { fields } => {
1,415✔
174
            let kind = quote!(#facet_crate::StructKind::Struct);
1,415✔
175
            let fields_vec = fields
1,415✔
176
                .iter()
1,415✔
177
                .map(|field| {
2,300✔
178
                    gen_field_from_pfield(field, struct_name, &ps.container.bgp, None, &facet_crate)
2,300✔
179
                })
2,300✔
180
                .collect::<Vec<_>>();
1,415✔
181
            (kind, fields_vec)
1,415✔
182
        }
183
        PStructKind::TupleStruct { fields } => {
87✔
184
            let kind = quote!(#facet_crate::StructKind::TupleStruct);
87✔
185
            let fields_vec = fields
87✔
186
                .iter()
87✔
187
                .map(|field| {
110✔
188
                    gen_field_from_pfield(field, struct_name, &ps.container.bgp, None, &facet_crate)
110✔
189
                })
110✔
190
                .collect::<Vec<_>>();
87✔
191
            (kind, fields_vec)
87✔
192
        }
193
        PStructKind::UnitStruct => {
194
            let kind = quote!(#facet_crate::StructKind::Unit);
8✔
195
            (kind, vec![])
8✔
196
        }
197
    };
198

199
    // Still need original AST for where clauses and type params for build_ helpers
200
    let where_clauses_ast = match &parsed.kind {
1,510✔
201
        StructKind::Struct { clauses, .. } => clauses.as_ref(),
1,415✔
202
        StructKind::TupleStruct { clauses, .. } => clauses.as_ref(),
87✔
203
        StructKind::UnitStruct { clauses, .. } => clauses.as_ref(),
8✔
204
    };
205
    let where_clauses = build_where_clauses(
1,510✔
206
        where_clauses_ast,
1,510✔
207
        parsed.generics.as_ref(),
1,510✔
208
        opaque,
1,510✔
209
        &facet_crate,
1,510✔
210
    );
211
    let type_params = build_type_params(parsed.generics.as_ref(), opaque, &facet_crate);
1,510✔
212

213
    // Static decl using PStruct BGP
214
    let static_decl = if ps.container.bgp.params.is_empty() {
1,510✔
215
        generate_static_decl(struct_name, &facet_crate)
1,445✔
216
    } else {
217
        TokenStream::new()
65✔
218
    };
219

220
    // Doc comments from PStruct
221
    let maybe_container_doc = if ps.container.attrs.doc.is_empty() {
1,510✔
222
        quote! {}
1,489✔
223
    } else {
224
        let doc_lines = ps.container.attrs.doc.iter().map(|s| quote!(#s));
95✔
225
        quote! { .doc(&[#(#doc_lines),*]) }
21✔
226
    };
227

228
    // Container attributes - most go through grammar dispatch
229
    // Filter out `invariants` and `crate` since they're handled specially
230
    let container_attributes_tokens = {
1,510✔
231
        let items: Vec<TokenStream> = ps
1,510✔
232
            .container
1,510✔
233
            .attrs
1,510✔
234
            .facet
1,510✔
235
            .iter()
1,510✔
236
            .filter(|attr| {
1,510✔
237
                // invariants is handled specially - it populates vtable.invariants
238
                // crate is handled specially - it sets the facet crate path
239
                !(attr.is_builtin()
73✔
240
                    && (attr.key_str() == "invariants" || attr.key_str() == "crate"))
71✔
241
            })
73✔
242
            .map(|attr| {
1,510✔
243
                let ext_attr = emit_attr(attr, &facet_crate);
67✔
244
                quote! { #ext_attr }
67✔
245
            })
67✔
246
            .collect();
1,510✔
247

248
        if items.is_empty() {
1,510✔
249
            quote! {}
1,451✔
250
        } else {
251
            quote! { .attributes(&const { [#(#items),*] }) }
59✔
252
        }
253
    };
254

255
    // Type tag from PStruct
256
    let type_tag_maybe = {
1,510✔
257
        if let Some(type_tag) = ps.container.attrs.get_builtin_args("type_tag") {
1,510✔
258
            quote! { .type_tag(#type_tag) }
8✔
259
        } else {
260
            quote! {}
1,502✔
261
        }
262
    };
263

264
    // Invariants from PStruct - extract invariant function expressions
265
    let invariant_maybe = {
1,510✔
266
        let invariant_exprs: Vec<&TokenStream> = ps
1,510✔
267
            .container
1,510✔
268
            .attrs
1,510✔
269
            .facet
1,510✔
270
            .iter()
1,510✔
271
            .filter(|attr| attr.is_builtin() && attr.key_str() == "invariants")
1,510✔
272
            .map(|attr| &attr.args)
1,510✔
273
            .collect();
1,510✔
274

275
        if !invariant_exprs.is_empty() {
1,510✔
276
            let tests = invariant_exprs.iter().map(|expr| {
3✔
277
                quote! {
3✔
278
                    if !#expr(value) {
279
                        return false;
280
                    }
281
                }
282
            });
3✔
283

284
            let bgp_display = ps.container.bgp.display_without_bounds();
3✔
285
            quote! {
3✔
286
                unsafe fn invariants<'mem>(value: #facet_crate::PtrConst<'mem>) -> bool {
287
                    let value = value.get::<#struct_name_ident #bgp_display>();
288
                    #(#tests)*
289
                    true
290
                }
291

292
                {
293
                    vtable.invariants = Some(invariants);
294
                }
295
            }
296
        } else {
297
            quote! {}
1,507✔
298
        }
299
    };
300

301
    // Transparent logic using PStruct
302
    let inner_field = if ps.container.attrs.has_builtin("transparent") {
1,510✔
303
        match &ps.kind {
28✔
304
            PStructKind::TupleStruct { fields } => {
28✔
305
                if fields.len() > 1 {
28✔
UNCOV
306
                    return quote! {
×
307
                        compile_error!("Transparent structs must be tuple structs with zero or one field");
308
                    };
309
                }
28✔
310
                fields.first().cloned() // Use first field if it exists, None otherwise (ZST case)
28✔
311
            }
312
            _ => {
UNCOV
313
                return quote! {
×
314
                    compile_error!("Transparent structs must be tuple structs");
315
                };
316
            }
317
        }
318
    } else {
319
        None
1,482✔
320
    };
321

322
    // Add try_from_inner implementation for transparent types
323
    let try_from_inner_code = if ps.container.attrs.has_builtin("transparent") {
1,510✔
324
        if let Some(inner_field) = &inner_field {
28✔
325
            if !inner_field.attrs.has_builtin("opaque") {
28✔
326
                // Transparent struct with one field
327
                let inner_field_type = &inner_field.ty;
26✔
328
                let bgp_without_bounds = ps.container.bgp.display_without_bounds();
26✔
329

330
                quote! {
26✔
331
                    // Define the try_from function for the value vtable
332
                    unsafe fn try_from<'src, 'dst>(
333
                        src_ptr: #facet_crate::PtrConst<'src>,
334
                        src_shape: &'static #facet_crate::Shape,
335
                        dst: #facet_crate::PtrUninit<'dst>
336
                    ) -> Result<#facet_crate::PtrMut<'dst>, #facet_crate::TryFromError> {
337
                        // Try the inner type's try_from function if it exists
338
                        let inner_result = match <#inner_field_type as #facet_crate::Facet>::SHAPE.vtable.try_from {
339
                            Some(inner_try) => unsafe { (inner_try)(src_ptr, src_shape, dst) },
340
                            None => Err(#facet_crate::TryFromError::UnsupportedSourceShape {
341
                                src_shape,
342
                                expected: const { &[ &<#inner_field_type as #facet_crate::Facet>::SHAPE ] },
343
                            })
344
                        };
345

346
                        match inner_result {
347
                            Ok(result) => Ok(result),
348
                            Err(_) => {
349
                                // If inner_try failed, check if source shape is exactly the inner shape
350
                                if src_shape != <#inner_field_type as #facet_crate::Facet>::SHAPE {
351
                                    return Err(#facet_crate::TryFromError::UnsupportedSourceShape {
352
                                        src_shape,
353
                                        expected: const { &[ &<#inner_field_type as #facet_crate::Facet>::SHAPE ] },
354
                                    });
355
                                }
356
                                // Read the inner value and construct the wrapper.
357
                                let inner: #inner_field_type = unsafe { src_ptr.read() };
358
                                Ok(unsafe { dst.put(inner) }) // Construct wrapper
359
                            }
360
                        }
361
                    }
362

363
                    // Define the try_into_inner function for the value vtable
364
                    unsafe fn try_into_inner<'src, 'dst>(
365
                        src_ptr: #facet_crate::PtrMut<'src>,
366
                        dst: #facet_crate::PtrUninit<'dst>
367
                    ) -> Result<#facet_crate::PtrMut<'dst>, #facet_crate::TryIntoInnerError> {
368
                        let wrapper = unsafe { src_ptr.get::<#struct_name_ident #bgp_without_bounds>() };
369
                        Ok(unsafe { dst.put(wrapper.0.clone()) }) // Assume tuple struct field 0
370
                    }
371

372
                    // Define the try_borrow_inner function for the value vtable
373
                    unsafe fn try_borrow_inner<'src>(
374
                        src_ptr: #facet_crate::PtrConst<'src>
375
                    ) -> Result<#facet_crate::PtrConst<'src>, #facet_crate::TryBorrowInnerError> {
376
                        let wrapper = unsafe { src_ptr.get::<#struct_name_ident #bgp_without_bounds>() };
377
                        // Return a pointer to the inner field (field 0 for tuple struct)
378
                        Ok(#facet_crate::PtrConst::new(::core::ptr::NonNull::from(&wrapper.0)))
379
                    }
380

381
                    {
382
                        vtable.try_from = Some(try_from);
383
                        vtable.try_into_inner = Some(try_into_inner);
384
                        vtable.try_borrow_inner = Some(try_borrow_inner);
385
                    }
386
                }
387
            } else {
388
                quote! {} // No try_from can be done for opaque
2✔
389
            }
390
        } else {
391
            // Transparent ZST struct (like struct Unit;)
UNCOV
392
            quote! {
×
393
                // Define the try_from function for the value vtable (ZST case)
394
                unsafe fn try_from<'src, 'dst>(
395
                    src_ptr: #facet_crate::PtrConst<'src>,
396
                    src_shape: &'static #facet_crate::Shape,
397
                    dst: #facet_crate::PtrUninit<'dst>
398
                ) -> Result<#facet_crate::PtrMut<'dst>, #facet_crate::TryFromError> {
399
                    if src_shape.layout.size() == 0 {
400
                         Ok(unsafe { dst.put(#struct_name_ident) }) // Construct ZST
401
                    } else {
402
                        Err(#facet_crate::TryFromError::UnsupportedSourceShape {
403
                            src_shape,
404
                            expected: const { &[ <() as #facet_crate::Facet>::SHAPE ] }, // Expect unit-like shape
405
                        })
406
                    }
407
                }
408

409
                {
410
                    vtable.try_from = Some(try_from);
411
                }
412

413
                // ZSTs cannot be meaningfully borrowed or converted *into* an inner value
414
                // try_into_inner and try_borrow_inner remain None
415
            }
416
        }
417
    } else {
418
        quote! {} // Not transparent
1,482✔
419
    };
420

421
    // Generate the inner shape function for transparent types
422
    let inner_setter = if ps.container.attrs.has_builtin("transparent") {
1,510✔
423
        let inner_shape_val = if let Some(inner_field) = &inner_field {
28✔
424
            let ty = &inner_field.ty;
28✔
425
            if inner_field.attrs.has_builtin("opaque") {
28✔
426
                quote! { <#facet_crate::Opaque<#ty> as #facet_crate::Facet>::SHAPE }
2✔
427
            } else {
428
                quote! { <#ty as #facet_crate::Facet>::SHAPE }
26✔
429
            }
430
        } else {
431
            // Transparent ZST case
UNCOV
432
            quote! { <() as #facet_crate::Facet>::SHAPE }
×
433
        };
434
        quote! { .inner(#inner_shape_val) }
28✔
435
    } else {
436
        quote! {}
1,482✔
437
    };
438

439
    // Generics from PStruct
440
    let facet_bgp = ps
1,510✔
441
        .container
1,510✔
442
        .bgp
1,510✔
443
        .with_lifetime(LifetimeName(format_ident!("__facet")));
1,510✔
444
    let bgp_def = facet_bgp.display_with_bounds();
1,510✔
445
    let bgp_without_bounds = ps.container.bgp.display_without_bounds();
1,510✔
446

447
    let (ty, fields) = if opaque {
1,510✔
448
        (
2✔
449
            quote! {
2✔
450
                .ty(#facet_crate::Type::User(#facet_crate::UserType::Opaque))
2✔
451
            },
2✔
452
            quote! {},
2✔
453
        )
2✔
454
    } else {
455
        (
456
            quote! {
1,508✔
457
                .ty(#facet_crate::Type::User(#facet_crate::UserType::Struct(#facet_crate::StructType::builder()
458
                    .repr(#repr)
459
                    .kind(#kind)
460
                    .fields(fields)
461
                    .build()
462
                )))
463
            },
464
            quote! {
1,508✔
465
                let fields: &'static [#facet_crate::Field] = &const {[#(#fields_vec),*]};
466
            },
467
        )
468
    };
469

470
    // Final quote block using refactored parts
471
    let result = quote! {
1,510✔
472
        #static_decl
473

474
        #[automatically_derived]
475
        unsafe impl #bgp_def #facet_crate::Facet<'__facet> for #struct_name_ident #bgp_without_bounds #where_clauses {
476
            const SHAPE: &'static #facet_crate::Shape = &const {
477
                #fields
478

479
                #facet_crate::Shape::builder_for_sized::<Self>()
480
                    .vtable({
481
                        let mut vtable = #facet_crate::value_vtable!(Self, #type_name_fn);
482
                        #invariant_maybe
483
                        #try_from_inner_code // Use the generated code for transparent types
484
                        vtable
485
                    })
486
                    .type_identifier(#struct_name_str)
487
                    #type_params // Still from parsed.generics
488
                    #ty
489
                    #inner_setter // Use transparency flag from PStruct
490
                    #maybe_container_doc // From ps.container.attrs.doc
491
                    #container_attributes_tokens // From ps.container.attrs.facet
492
                    #type_tag_maybe
493
                    .build()
494
            };
495
        }
496
    };
497

498
    result
1,510✔
499
}
1,510✔
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