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

facet-rs / facet / 14714481859

28 Apr 2025 05:56PM UTC coverage: 58.675% (+0.8%) from 57.844%
14714481859

Pull #472

github

web-flow
Merge 1e036f77e into ce0941010
Pull Request #472: Parse everything about structs/containers

189 of 208 new or added lines in 2 files covered. (90.87%)

4 existing lines in 2 files now uncovered.

7903 of 13469 relevant lines covered (58.68%)

80.68 hits per line

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

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

4
/// Processes a regular struct to implement Facet
5
///
6
/// Example input:
7
/// ```rust
8
/// struct Blah {
9
///     foo: u32,
10
///     bar: String,
11
/// }
12
/// ```
13
pub(crate) fn process_struct(parsed: Struct) -> TokenStream {
275✔
14
    let _pstruct = PStruct::parse(&parsed);
275✔
15

275✔
16
    let is_transparent = parsed.is_transparent();
275✔
17
    let struct_name = parsed.name.to_string();
275✔
18

275✔
19
    // Generate field definitions
275✔
20
    let bgp = BoundedGenericParams::parse(parsed.generics.as_ref());
275✔
21

275✔
22
    let kind;
275✔
23
    let where_clauses;
275✔
24
    let type_params = build_type_params(parsed.generics.as_ref());
275✔
25
    let container_attributes = build_container_attributes(&parsed.attributes);
275✔
26

27
    // For transparent, extract the inner type
28
    let inner_type = if is_transparent {
275✔
29
        match &parsed.kind {
2✔
30
            StructKind::TupleStruct { fields, .. } => {
2✔
31
                if fields.content.0.len() != 1 {
2✔
32
                    panic!("Transparent structs must have exactly one field");
×
33
                }
2✔
34
                fields.content.0[0].value.typ.tokens_to_string()
2✔
35
            }
UNCOV
36
            _ => panic!("Transparent structs must be tuple structs with a single field"),
×
37
        }
38
    } else {
39
        String::new() // Not used for non-transparent
273✔
40
    };
41

42
    let fields = match &parsed.kind {
275✔
43
        StructKind::Struct { clauses, fields } => {
235✔
44
            kind = "::facet::StructKind::Struct";
235✔
45
            where_clauses = clauses.as_ref();
235✔
46
            fields
235✔
47
                .content
235✔
48
                .0
235✔
49
                .iter()
235✔
50
                .map(|field| {
411✔
51
                    // Handle raw identifiers (like r#type) by stripping the 'r#' prefix.
411✔
52
                    let raw_field_name = field.value.name.to_string(); // e.g., "r#type"
411✔
53
                    let normalized_field_name = normalize_ident_str(&raw_field_name); // e.g., "type"
411✔
54
                    gen_struct_field(FieldInfo {
411✔
55
                        raw_field_name: &raw_field_name,
411✔
56
                        normalized_field_name,
411✔
57
                        field_type: &field.value.typ.tokens_to_string(),
411✔
58
                        struct_name: &struct_name,
411✔
59
                        bgp: &bgp,
411✔
60
                        attrs: &field.value.attributes,
411✔
61
                        base_field_offset: None,
411✔
62
                        rename_rule: container_attributes.rename_rule,
411✔
63
                    })
411✔
64
                })
411✔
65
                .collect::<Vec<String>>()
235✔
66
        }
67
        StructKind::TupleStruct {
68
            fields,
36✔
69
            clauses,
36✔
70
            semi: _,
36✔
71
        } => {
36✔
72
            kind = "::facet::StructKind::TupleStruct";
36✔
73
            where_clauses = clauses.as_ref();
36✔
74
            fields
36✔
75
                .content
36✔
76
                .0
36✔
77
                .iter()
36✔
78
                .enumerate()
36✔
79
                .map(|(index, field)| {
55✔
80
                    let field_name = format!("{index}");
55✔
81
                    gen_struct_field(FieldInfo {
55✔
82
                        raw_field_name: &field_name,
55✔
83
                        normalized_field_name: &field_name,
55✔
84
                        field_type: &field.value.typ.tokens_to_string(),
55✔
85
                        struct_name: &struct_name,
55✔
86
                        bgp: &bgp,
55✔
87
                        attrs: &field.value.attributes,
55✔
88
                        base_field_offset: None,
55✔
89
                        rename_rule: container_attributes.rename_rule,
55✔
90
                    })
55✔
91
                })
55✔
92
                .collect::<Vec<String>>()
36✔
93
        }
94
        StructKind::UnitStruct { clauses, semi: _ } => {
4✔
95
            kind = "::facet::StructKind::Unit";
4✔
96
            where_clauses = clauses.as_ref();
4✔
97
            vec![]
4✔
98
        }
99
    }
100
    .join(", ");
275✔
101

275✔
102
    let where_clauses = build_where_clauses(where_clauses, parsed.generics.as_ref());
275✔
103
    let static_decl = if parsed.generics.is_none() {
275✔
104
        generate_static_decl(&struct_name)
251✔
105
    } else {
106
        String::new()
24✔
107
    };
108
    let maybe_container_doc = build_maybe_doc(&parsed.attributes);
275✔
109

275✔
110
    let mut invariant_maybe = "".to_string();
275✔
111
    let invariant_attrs = parsed
275✔
112
        .attributes
275✔
113
        .iter()
275✔
114
        .filter_map(|attr| match &attr.body.content {
275✔
115
            AttributeInner::Facet(facet_attr) => match &facet_attr.inner.content {
20✔
116
                FacetInner::Invariants(invariant_inner) => Some(invariant_inner),
1✔
117
                _ => None,
19✔
118
            },
119
            _ => None,
83✔
120
        })
103✔
121
        .collect::<Vec<_>>();
275✔
122

275✔
123
    if !invariant_attrs.is_empty() {
275✔
124
        let tests = invariant_attrs
1✔
125
            .iter()
1✔
126
            .map(|invariant| {
1✔
127
                let invariant_name = invariant.value.as_str();
1✔
128
                format!(
1✔
129
                    r#"
1✔
130
                    if !value.{invariant_name}() {{
1✔
131
                        return false;
1✔
132
                    }}
1✔
133
                    "#
1✔
134
                )
1✔
135
            })
1✔
136
            .collect::<Vec<_>>()
1✔
137
            .join("\n");
1✔
138

1✔
139
        let invariant_fn = format!(
1✔
140
            r#"
1✔
141
            unsafe fn invariants<'mem>(value: ::facet::PtrConst<'mem>) -> bool {{
1✔
142
                let value = value.get::<{struct_name}{bgp}>();
1✔
143
                {tests}
1✔
144
                true
1✔
145
            }}
1✔
146
            "#,
1✔
147
            bgp = bgp.display_without_bounds(),
1✔
148
        );
1✔
149

1✔
150
        invariant_maybe = format!(
1✔
151
            r#"
1✔
152
            {invariant_fn}
1✔
153

1✔
154
            vtable.invariants = Some(invariants);
1✔
155
            "#
1✔
156
        );
157
    }
274✔
158

159
    // Add try_from_inner implementation for transparent types
160
    let try_from_inner_code = if is_transparent {
275✔
161
        format!(
2✔
162
            r#"
2✔
163
            // Define the try_from function for the value vtable
2✔
164
            unsafe fn try_from<'src, 'dst>(
2✔
165
                src_ptr: ::facet::PtrConst<'src>,
2✔
166
                src_shape: &'static ::facet::Shape,
2✔
167
                dst: ::facet::PtrUninit<'dst>
2✔
168
            ) -> Result<::facet::PtrMut<'dst>, ::facet::TryFromError> {{
2✔
169
                match <{inner_type} as ::facet::Facet>::SHAPE.vtable.try_from {{
2✔
170
                    Some(inner_try) => {{
2✔
171
                        unsafe {{
2✔
172
                            (inner_try)(src_ptr, src_shape, dst)
2✔
173
                        }}
2✔
174
                    }},
2✔
175
                    None => {{
2✔
176
                        if src_shape != <{inner_type} as ::facet::Facet>::SHAPE {{
2✔
177
                            return Err(::facet::TryFromError::UnsupportedSourceShape {{
2✔
178
                                src_shape,
2✔
179
                                expected: const {{ &[ &<{inner_type} as ::facet::Facet>::SHAPE ] }},
2✔
180
                            }});
2✔
181
                        }}
2✔
182

2✔
183
                        let inner: {inner_type} = unsafe {{ src_ptr.read() }};
2✔
184
                        Ok(unsafe {{ dst.put(inner) }})
2✔
185
                    }}
2✔
186
                }}
2✔
187
            }}
2✔
188

2✔
189
            vtable.try_from = Some(try_from);
2✔
190

2✔
191
            // Define the try_into_inner function for the value vtable
2✔
192
            unsafe fn try_into_inner<'src, 'dst>(
2✔
193
                src_ptr: ::facet::PtrConst<'src>,
2✔
194
                dst: ::facet::PtrUninit<'dst>
2✔
195
            ) -> Result<::facet::PtrMut<'dst>, ::facet::TryIntoInnerError> {{
2✔
196
                // Get the wrapper value we're converting from
2✔
197
                let wrapper = unsafe {{ src_ptr.get::<{struct_name}{bgp_without_bounds}>() }};
2✔
198
                // Extract inner value and put it in the destination
2✔
199
                Ok(unsafe {{ dst.put(wrapper.0.clone()) }})
2✔
200
            }}
2✔
201

2✔
202
            vtable.try_into_inner = Some(try_into_inner);
2✔
203

2✔
204
            // Define the try_borrow_inner function for the value vtable
2✔
205
            unsafe fn try_borrow_inner<'src>(
2✔
206
                src_ptr: ::facet::PtrConst<'src>
2✔
207
            ) -> Result<::facet::PtrConst<'src>, ::facet::TryBorrowInnerError> {{
2✔
208
                // Get the wrapper value
2✔
209
                let wrapper = unsafe {{ src_ptr.get::<{struct_name}{bgp_without_bounds}>() }};
2✔
210
                // Return a pointer to the inner value
2✔
211
                Ok(::facet::PtrConst::new(&wrapper.0 as *const _ as *const u8))
2✔
212
            }}
2✔
213

2✔
214
            vtable.try_borrow_inner = Some(try_borrow_inner);
2✔
215
            "#,
2✔
216
            bgp_without_bounds = bgp.display_without_bounds(),
2✔
217
        )
218
    } else {
219
        String::new()
273✔
220
    };
221

222
    // Generate the inner shape function for transparent types
223
    let inner_shape_fn = if is_transparent {
275✔
224
        format!(
2✔
225
            r#"
2✔
226
        // Function to return inner type's shape
2✔
227
        fn inner_shape() -> &'static ::facet::Shape {{
2✔
228
            <{inner_type} as ::facet::Facet>::SHAPE
2✔
229
        }}
2✔
230
            "#
2✔
231
        )
232
    } else {
233
        String::new()
273✔
234
    };
235

236
    // Generate the impl
237
    let output = format!(
275✔
238
        r#"
275✔
239
{static_decl}
275✔
240

275✔
241
#[automatically_derived]
275✔
242
unsafe impl{bgp_def} ::facet::Facet<'__facet> for {struct_name}{bgp_without_bounds} {where_clauses} {{
275✔
243
    const SHAPE: &'static ::facet::Shape = &const {{
275✔
244
        let fields: &'static [::facet::Field] = &const {{[{fields}]}};
275✔
245

275✔
246
        let vtable = &const {{
275✔
247
            let mut vtable = ::facet::value_vtable!(
275✔
248
                Self,
275✔
249
                |f, _opts| ::core::fmt::Write::write_str(f, "{struct_name}")
275✔
250
            );
275✔
251
            {invariant_maybe}
275✔
252
            {try_from_inner_code}
275✔
253
            vtable
275✔
254
        }};
275✔
255

275✔
256
{inner_shape_fn}
275✔
257

275✔
258
        ::facet::Shape::builder()
275✔
259
            .id(::facet::ConstTypeId::of::<Self>())
275✔
260
            .layout(::core::alloc::Layout::new::<Self>())
275✔
261
            {type_params}
275✔
262
            .vtable(vtable)
275✔
263
            .def(::facet::Def::Struct(::facet::StructDef::builder()
275✔
264
                .kind({kind})
275✔
265
                .fields(fields)
275✔
266
                .build()))
275✔
267
            {inner_setter}
275✔
268
            {maybe_container_doc}
275✔
269
            {container_attributes}
275✔
270
            .build()
275✔
271
    }};
275✔
272
}}
275✔
273
        "#,
275✔
274
        bgp_def = bgp.with_lifetime("__facet").display_with_bounds(),
275✔
275
        bgp_without_bounds = bgp.display_without_bounds(),
275✔
276
        inner_setter = if is_transparent {
275✔
277
            ".inner(inner_shape)"
2✔
278
        } else {
279
            ""
273✔
280
        },
281
        container_attributes = container_attributes.code
282
    );
283

284
    // Uncomment to see generated code before lexin
285
    // panic!("output =\n{output}");
286

287
    output.into_token_stream()
275✔
288
}
275✔
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