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

facet-rs / facet / 14665423283

25 Apr 2025 01:11PM UTC coverage: 57.733% (-0.5%) from 58.213%
14665423283

Pull #390

github

web-flow
Merge 91f4b0118 into baf8b033b
Pull Request #390: 🧨 (big one) Introduce `Shape::inner` for (Utf8PathBuf, NonZero<u8> etc.) along with try_from_inner, try_into_inner, borrow_inner etc.

196 of 360 new or added lines in 17 files covered. (54.44%)

47 existing lines in 4 files now uncovered.

6559 of 11361 relevant lines covered (57.73%)

73.03 hits per line

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

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

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

284✔
16
    // Generate field definitions
284✔
17
    let bgp = BoundedGenericParams::parse(parsed.generics.as_ref());
284✔
18

284✔
19
    let kind;
284✔
20
    let where_clauses;
284✔
21
    let type_params = build_type_params(parsed.generics.as_ref());
284✔
22
    let set_attributes = build_container_attributes(&parsed.attributes);
284✔
23

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

39
    let fields = match &parsed.kind {
284✔
40
        StructKind::Struct { clauses, fields } => {
230✔
41
            kind = "::facet::StructKind::Struct";
230✔
42
            where_clauses = clauses.as_ref();
230✔
43
            fields
230✔
44
                .content
230✔
45
                .0
230✔
46
                .iter()
230✔
47
                .map(|field| {
392✔
48
                    let field_name = field.value.name.to_string();
392✔
49
                    gen_struct_field(
392✔
50
                        &field_name,
392✔
51
                        &field.value.typ.tokens_to_string(),
392✔
52
                        &struct_name,
392✔
53
                        &bgp,
392✔
54
                        &field.value.attributes,
392✔
55
                        None,
392✔
56
                    )
392✔
57
                })
392✔
58
                .collect::<Vec<String>>()
230✔
59
        }
60
        StructKind::TupleStruct {
61
            fields,
48✔
62
            clauses,
48✔
63
            semi: _,
48✔
64
        } => {
48✔
65
            kind = "::facet::StructKind::TupleStruct";
48✔
66
            where_clauses = clauses.as_ref();
48✔
67
            fields
48✔
68
                .content
48✔
69
                .0
48✔
70
                .iter()
48✔
71
                .enumerate()
48✔
72
                .map(|(index, field)| {
77✔
73
                    let field_name = format!("{index}");
77✔
74
                    gen_struct_field(
77✔
75
                        &field_name,
77✔
76
                        &field.value.typ.tokens_to_string(),
77✔
77
                        &struct_name,
77✔
78
                        &bgp,
77✔
79
                        &field.value.attributes,
77✔
80
                        None,
77✔
81
                    )
77✔
82
                })
77✔
83
                .collect::<Vec<String>>()
48✔
84
        }
85
        StructKind::UnitStruct { clauses, semi: _ } => {
6✔
86
            kind = "::facet::StructKind::Unit";
6✔
87
            where_clauses = clauses.as_ref();
6✔
88
            vec![]
6✔
89
        }
90
    }
91
    .join(", ");
284✔
92

284✔
93
    let where_clauses = build_where_clauses(where_clauses, parsed.generics.as_ref());
284✔
94
    let static_decl = if parsed.generics.is_none() {
284✔
95
        generate_static_decl(&struct_name)
253✔
96
    } else {
97
        String::new()
31✔
98
    };
99
    let maybe_container_doc = build_maybe_doc(&parsed.attributes);
284✔
100

284✔
101
    let mut invariant_maybe = "".to_string();
284✔
102
    let invariant_attrs = parsed
284✔
103
        .attributes
284✔
104
        .iter()
284✔
105
        .filter_map(|attr| match &attr.body.content {
284✔
106
            AttributeInner::Facet(facet_attr) => match &facet_attr.inner.content {
12✔
107
                FacetInner::Invariants(invariant_inner) => Some(invariant_inner),
1✔
108
                _ => None,
11✔
109
            },
110
            _ => None,
128✔
111
        })
140✔
112
        .collect::<Vec<_>>();
284✔
113

284✔
114
    if !invariant_attrs.is_empty() {
284✔
115
        let tests = invariant_attrs
1✔
116
            .iter()
1✔
117
            .map(|invariant| {
1✔
118
                let invariant_name = invariant.value.as_str();
1✔
119
                format!(
1✔
120
                    r#"
1✔
121
                    if !value.{invariant_name}() {{
1✔
122
                        return false;
1✔
123
                    }}
1✔
124
                    "#
1✔
125
                )
1✔
126
            })
1✔
127
            .collect::<Vec<_>>()
1✔
128
            .join("\n");
1✔
129

1✔
130
        let invariant_fn = format!(
1✔
131
            r#"
1✔
132
            unsafe fn invariants<'mem>(value: ::facet::PtrConst<'mem>) -> bool {{
1✔
133
                let value = value.get::<{struct_name}{bgp}>();
1✔
134
                {tests}
1✔
135
                true
1✔
136
            }}
1✔
137
            "#,
1✔
138
            bgp = bgp.display_without_bounds(),
1✔
139
        );
1✔
140

1✔
141
        invariant_maybe = format!(
1✔
142
            r#"
1✔
143
            {invariant_fn}
1✔
144

1✔
145
            vtable.invariants = Some(invariants);
1✔
146
            "#
1✔
147
        );
148
    }
283✔
149

150
    // Add try_from_inner implementation for transparent types
151
    let try_from_inner_code = if is_transparent {
284✔
152
        format!(
3✔
153
            r#"
3✔
154
            // Define the try_from_inner function for the value vtable
3✔
155
            unsafe fn try_from_inner<'src, 'dst>(
3✔
156
                src_ptr: ::facet::PtrConst<'src>,
3✔
157
                src_shape: &'static ::facet::Shape,
3✔
158
                dst: ::facet::PtrUninit<'dst>
3✔
159
            ) -> Result<::facet::PtrMut<'dst>, ::facet::TryFromInnerError> {{
3✔
160
                // Check if source shape matches inner type's shape
3✔
161
                if src_shape.id != <{inner_type} as ::facet::Facet>::SHAPE.id {{
3✔
162
                    return Err(::facet::TryFromInnerError::UnsupportedSourceShape {{
3✔
163
                        src_shape,
3✔
164
                        expected: &[<{inner_type} as ::facet::Facet>::SHAPE],
3✔
165
                    }});
3✔
166
                }}
3✔
167

3✔
168
                // Get the inner value and create our newtype wrapper
3✔
169
                let inner_val = unsafe {{ src_ptr.get::<{inner_type}>() }};
3✔
170
                // Put the inner value into the wrapper and return the destination
3✔
171
                Ok(unsafe {{ dst.put({struct_name}{bgp_without_bounds}(inner_val.clone())) }})
3✔
172
            }}
3✔
173

3✔
174
            vtable.try_from_inner = Some(try_from_inner);
3✔
175

3✔
176
            // Define the try_into_inner function for the value vtable
3✔
177
            unsafe fn try_into_inner<'src, 'dst>(
3✔
178
                src_ptr: ::facet::PtrConst<'src>,
3✔
179
                dst: ::facet::PtrUninit<'dst>
3✔
180
            ) -> Result<::facet::PtrMut<'dst>, ::facet::TryIntoInnerError> {{
3✔
181
                // Get the wrapper value we're converting from
3✔
182
                let wrapper = unsafe {{ src_ptr.get::<{struct_name}{bgp_without_bounds}>() }};
3✔
183
                // Extract inner value and put it in the destination
3✔
184
                Ok(unsafe {{ dst.put(wrapper.0.clone()) }})
3✔
185
            }}
3✔
186

3✔
187
            vtable.try_into_inner = Some(try_into_inner);
3✔
188

3✔
189
            // Define the try_borrow_inner function for the value vtable
3✔
190
            unsafe fn try_borrow_inner<'src>(
3✔
191
                src_ptr: ::facet::PtrConst<'src>
3✔
192
            ) -> Result<::facet::PtrConst<'src>, ::facet::TryBorrowInnerError> {{
3✔
193
                // Get the wrapper value
3✔
194
                let wrapper = unsafe {{ src_ptr.get::<{struct_name}{bgp_without_bounds}>() }};
3✔
195
                // Return a pointer to the inner value
3✔
196
                Ok(::facet::PtrConst::new(&wrapper.0 as *const _ as *const u8))
3✔
197
            }}
3✔
198

3✔
199
            vtable.try_borrow_inner = Some(try_borrow_inner);
3✔
200
            "#,
3✔
201
            bgp_without_bounds = bgp.display_without_bounds(),
3✔
202
        )
203
    } else {
204
        String::new()
281✔
205
    };
206

207
    // Generate the inner shape function for transparent types
208
    let inner_shape_fn = if is_transparent {
284✔
209
        format!(
3✔
210
            r#"
3✔
211
        // Function to return inner type's shape
3✔
212
        fn inner_shape() -> &'static ::facet::Shape {{
3✔
213
            <{inner_type} as ::facet::Facet>::SHAPE
3✔
214
        }}
3✔
215
            "#
3✔
216
        )
217
    } else {
218
        String::new()
281✔
219
    };
220

221
    // Generate the impl
222
    let output = format!(
284✔
223
        r#"
284✔
224
{static_decl}
284✔
225

284✔
226
#[automatically_derived]
284✔
227
unsafe impl{bgp_def} ::facet::Facet<'__facet> for {struct_name}{bgp_without_bounds} {where_clauses} {{
284✔
228
    const SHAPE: &'static ::facet::Shape = &const {{
284✔
229
        let fields: &'static [::facet::Field] = &const {{[{fields}]}};
284✔
230

284✔
231
        let vtable = &const {{
284✔
232
            let mut vtable = ::facet::value_vtable_inner!(
284✔
233
                Self,
284✔
234
                |f, _opts| ::core::fmt::Write::write_str(f, "{struct_name}")
284✔
235
            );
284✔
236
            {invariant_maybe}
284✔
237
            {try_from_inner_code}
284✔
238
            vtable
284✔
239
        }};
284✔
240

284✔
241
{inner_shape_fn}
284✔
242

284✔
243
        ::facet::Shape::builder()
284✔
244
            .id(::facet::ConstTypeId::of::<Self>())
284✔
245
            .layout(::core::alloc::Layout::new::<Self>())
284✔
246
            {type_params}
284✔
247
            .vtable(vtable)
284✔
248
            .def(::facet::Def::Struct(::facet::StructDef::builder()
284✔
249
                .kind({kind})
284✔
250
                .fields(fields)
284✔
251
                .build()))
284✔
252
            {inner_setter}
284✔
253
            {maybe_container_doc}
284✔
254
            {set_attributes}
284✔
255
            .build()
284✔
256
    }};
284✔
257
}}
284✔
258
        "#,
284✔
259
        bgp_def = bgp.with_lifetime("__facet").display_with_bounds(),
284✔
260
        bgp_without_bounds = bgp.display_without_bounds(),
284✔
261
        inner_setter = if is_transparent {
284✔
262
            ".inner(inner_shape)"
3✔
263
        } else {
264
            ""
281✔
265
        },
266
    );
267

268
    // Uncomment to see generated code before lexin
269
    // panic!("output =\n{output}");
270

271
    output.into_token_stream()
284✔
272
}
284✔
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