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

facet-rs / facet / 20134750802

11 Dec 2025 01:29PM UTC coverage: 57.796% (-0.1%) from 57.906%
20134750802

push

github

fasterthanlime
feat: Add concept of truthiness

The easiest way to tell if something should be serialized or not!

61 of 197 new or added lines in 14 files covered. (30.96%)

1 existing line in 1 file now uncovered.

28969 of 50123 relevant lines covered (57.8%)

6281.89 hits per line

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

78.53
/facet-core/src/impls/core/array.rs
1
//! Facet implementation for [T; N] arrays
2

3
use core::{cmp::Ordering, fmt};
4

5
use crate::{
6
    ArrayDef, ArrayVTable, Def, Facet, HashProxy, OxPtrConst, OxPtrMut, OxRef, PtrConst, PtrMut,
7
    Shape, ShapeBuilder, Type, TypeNameOpts, TypeOpsIndirect, TypeParam, VTableIndirect,
8
};
9

10
/// Extract the ArrayDef from a shape, returns None if not an array
11
#[inline]
12
fn get_array_def(shape: &'static Shape) -> Option<&'static ArrayDef> {
28✔
13
    match shape.def {
28✔
14
        Def::Array(ref def) => Some(def),
28✔
15
        _ => None,
×
16
    }
17
}
28✔
18

19
/// Type-erased type_name for arrays - reads T and N from the shape
20
fn array_type_name(
32✔
21
    shape: &'static Shape,
32✔
22
    f: &mut fmt::Formatter<'_>,
32✔
23
    opts: TypeNameOpts,
32✔
24
) -> fmt::Result {
32✔
25
    let def = match &shape.def {
32✔
26
        Def::Array(def) => def,
32✔
27
        _ => return write!(f, "[?; ?]"),
×
28
    };
29

30
    if let Some(opts) = opts.for_children() {
32✔
31
        write!(f, "[")?;
32✔
32
        def.t.write_type_name(f, opts)?;
32✔
33
        write!(f, "; {}]", def.n)
32✔
34
    } else {
35
        write!(f, "[…; {}]", def.n)
×
36
    }
37
}
32✔
38

39
/// Debug for [T; N] - formats as array literal
40
unsafe fn array_debug(
11✔
41
    ox: OxPtrConst,
11✔
42
    f: &mut core::fmt::Formatter<'_>,
11✔
43
) -> Option<core::fmt::Result> {
11✔
44
    let shape = ox.shape();
11✔
45
    let def = get_array_def(shape)?;
11✔
46
    let ptr = ox.ptr();
11✔
47

48
    let mut list = f.debug_list();
11✔
49
    let slice_ptr = unsafe { (def.vtable.as_ptr)(ptr) };
11✔
50
    let stride = def.t.layout.sized_layout().ok()?.pad_to_align().size();
11✔
51

52
    for i in 0..def.n {
11✔
53
        let elem_ptr = unsafe { PtrConst::new((slice_ptr.as_byte_ptr()).add(i * stride)) };
11✔
54
        let elem_ox = OxRef::new(elem_ptr, def.t);
11✔
55
        list.entry(&elem_ox);
11✔
56
    }
11✔
57
    Some(list.finish())
11✔
58
}
11✔
59

60
/// Hash for [T; N] - hashes each element
61
unsafe fn array_hash(ox: OxPtrConst, hasher: &mut HashProxy<'_>) -> Option<()> {
×
62
    let shape = ox.shape();
×
63
    let def = get_array_def(shape)?;
×
64
    let ptr = ox.ptr();
×
65

66
    let slice_ptr = unsafe { (def.vtable.as_ptr)(ptr) };
×
67
    let stride = def.t.layout.sized_layout().ok()?.pad_to_align().size();
×
68

69
    for i in 0..def.n {
×
70
        let elem_ptr = unsafe { PtrConst::new((slice_ptr.as_byte_ptr()).add(i * stride)) };
×
71
        unsafe { def.t.call_hash(elem_ptr, hasher)? };
×
72
    }
73
    Some(())
×
74
}
×
75

76
/// PartialEq for [T; N]
77
unsafe fn array_partial_eq(a: OxPtrConst, b: OxPtrConst) -> Option<bool> {
4✔
78
    let shape = a.shape();
4✔
79
    let def = get_array_def(shape)?;
4✔
80

81
    let a_ptr = unsafe { (def.vtable.as_ptr)(a.ptr()) };
4✔
82
    let b_ptr = unsafe { (def.vtable.as_ptr)(b.ptr()) };
4✔
83
    let stride = def.t.layout.sized_layout().ok()?.pad_to_align().size();
4✔
84

85
    for i in 0..def.n {
7✔
86
        let a_elem = unsafe { PtrConst::new((a_ptr.as_byte_ptr()).add(i * stride)) };
7✔
87
        let b_elem = unsafe { PtrConst::new((b_ptr.as_byte_ptr()).add(i * stride)) };
7✔
88
        if !unsafe { def.t.call_partial_eq(a_elem, b_elem)? } {
7✔
89
            return Some(false);
3✔
90
        }
4✔
91
    }
92
    Some(true)
1✔
93
}
4✔
94

95
/// PartialOrd for [T; N]
96
unsafe fn array_partial_cmp(a: OxPtrConst, b: OxPtrConst) -> Option<Option<Ordering>> {
2✔
97
    let shape = a.shape();
2✔
98
    let def = get_array_def(shape)?;
2✔
99

100
    let a_ptr = unsafe { (def.vtable.as_ptr)(a.ptr()) };
2✔
101
    let b_ptr = unsafe { (def.vtable.as_ptr)(b.ptr()) };
2✔
102
    let stride = def.t.layout.sized_layout().ok()?.pad_to_align().size();
2✔
103

104
    for i in 0..def.n {
2✔
105
        let a_elem = unsafe { PtrConst::new((a_ptr.as_byte_ptr()).add(i * stride)) };
2✔
106
        let b_elem = unsafe { PtrConst::new((b_ptr.as_byte_ptr()).add(i * stride)) };
2✔
107
        match unsafe { def.t.call_partial_cmp(a_elem, b_elem)? } {
2✔
108
            Some(Ordering::Equal) => continue,
×
109
            other => return Some(other),
2✔
110
        }
111
    }
112
    Some(Some(Ordering::Equal))
×
113
}
2✔
114

115
/// Ord for [T; N]
116
unsafe fn array_cmp(a: OxPtrConst, b: OxPtrConst) -> Option<Ordering> {
2✔
117
    let shape = a.shape();
2✔
118
    let def = get_array_def(shape)?;
2✔
119

120
    let a_ptr = unsafe { (def.vtable.as_ptr)(a.ptr()) };
2✔
121
    let b_ptr = unsafe { (def.vtable.as_ptr)(b.ptr()) };
2✔
122
    let stride = def.t.layout.sized_layout().ok()?.pad_to_align().size();
2✔
123

124
    for i in 0..def.n {
2✔
125
        let a_elem = unsafe { PtrConst::new((a_ptr.as_byte_ptr()).add(i * stride)) };
2✔
126
        let b_elem = unsafe { PtrConst::new((b_ptr.as_byte_ptr()).add(i * stride)) };
2✔
127
        match unsafe { def.t.call_cmp(a_elem, b_elem)? } {
2✔
128
            Ordering::Equal => continue,
×
129
            other => return Some(other),
2✔
130
        }
131
    }
132
    Some(Ordering::Equal)
×
133
}
2✔
134

135
/// Drop for [T; N]
136
unsafe fn array_drop(ox: OxPtrMut) {
4✔
137
    let shape = ox.shape();
4✔
138
    let Some(def) = get_array_def(shape) else {
4✔
139
        return;
×
140
    };
141
    let ptr = ox.ptr();
4✔
142

143
    let slice_ptr = unsafe { (def.vtable.as_mut_ptr)(ptr) };
4✔
144
    let Some(stride) = def
4✔
145
        .t
4✔
146
        .layout
4✔
147
        .sized_layout()
4✔
148
        .ok()
4✔
149
        .map(|l| l.pad_to_align().size())
4✔
150
    else {
151
        return;
×
152
    };
153

154
    for i in 0..def.n {
4✔
155
        let elem_ptr = unsafe { PtrMut::new((slice_ptr.as_byte_ptr() as *mut u8).add(i * stride)) };
4✔
156
        unsafe { def.t.call_drop_in_place(elem_ptr) };
4✔
157
    }
4✔
158
}
4✔
159

160
/// Default for [T; N] - default-initializes each element
161
unsafe fn array_default(ox: OxPtrMut) {
3✔
162
    let shape = ox.shape();
3✔
163
    let Some(def) = get_array_def(shape) else {
3✔
164
        return;
×
165
    };
166
    let ptr = ox.ptr();
3✔
167

168
    let slice_ptr = unsafe { (def.vtable.as_mut_ptr)(ptr) };
3✔
169
    let Some(stride) = def
3✔
170
        .t
3✔
171
        .layout
3✔
172
        .sized_layout()
3✔
173
        .ok()
3✔
174
        .map(|l| l.pad_to_align().size())
3✔
175
    else {
176
        return;
×
177
    };
178

179
    for i in 0..def.n {
3✔
180
        let elem_ptr = unsafe { PtrMut::new((slice_ptr.as_byte_ptr() as *mut u8).add(i * stride)) };
2✔
181
        if unsafe { def.t.call_default_in_place(elem_ptr) }.is_none() {
2✔
182
            return;
×
183
        }
2✔
184
    }
185
}
3✔
186

187
/// Clone for [T; N] - clones each element
188
unsafe fn array_clone(src: OxPtrConst, dst: OxPtrMut) {
2✔
189
    let shape = src.shape();
2✔
190
    let Some(def) = get_array_def(shape) else {
2✔
191
        return;
×
192
    };
193

194
    let src_ptr = unsafe { (def.vtable.as_ptr)(src.ptr()) };
2✔
195
    let dst_ptr = unsafe { (def.vtable.as_mut_ptr)(dst.ptr()) };
2✔
196
    let Some(stride) = def
2✔
197
        .t
2✔
198
        .layout
2✔
199
        .sized_layout()
2✔
200
        .ok()
2✔
201
        .map(|l| l.pad_to_align().size())
2✔
202
    else {
203
        return;
×
204
    };
205

206
    for i in 0..def.n {
2✔
207
        let src_elem = unsafe { PtrConst::new((src_ptr.as_byte_ptr()).add(i * stride)) };
2✔
208
        let dst_elem = unsafe { PtrMut::new((dst_ptr.as_byte_ptr() as *mut u8).add(i * stride)) };
2✔
209
        if unsafe { def.t.call_clone_into(src_elem, dst_elem) }.is_none() {
2✔
210
            return;
×
211
        }
2✔
212
    }
213
}
2✔
214

215
// Shared vtable for all [T; N]
216
const ARRAY_VTABLE: VTableIndirect = VTableIndirect {
217
    display: None,
218
    debug: Some(array_debug),
219
    hash: Some(array_hash),
220
    invariants: None,
221
    parse: None,
222
    try_from: None,
223
    try_into_inner: None,
224
    try_borrow_inner: None,
225
    partial_eq: Some(array_partial_eq),
226
    partial_cmp: Some(array_partial_cmp),
227
    cmp: Some(array_cmp),
228
};
229

230
/// Get pointer to array data buffer
231
unsafe fn array_as_ptr<T, const N: usize>(ptr: PtrConst) -> PtrConst {
61✔
232
    let array = unsafe { ptr.get::<[T; N]>() };
61✔
233
    PtrConst::new(array.as_ptr() as *const u8)
61✔
234
}
61✔
235

236
/// Get mutable pointer to array data buffer
237
unsafe fn array_as_mut_ptr<T, const N: usize>(ptr: PtrMut) -> PtrMut {
9✔
238
    let array = unsafe { ptr.as_mut::<[T; N]>() };
9✔
239
    PtrMut::new(array.as_mut_ptr() as *mut u8)
9✔
240
}
9✔
241

242
unsafe impl<'a, T, const N: usize> Facet<'a> for [T; N]
243
where
244
    T: Facet<'a>,
245
{
246
    const SHAPE: &'static Shape = &const {
247
        const fn build_array_vtable<T, const N: usize>() -> ArrayVTable {
×
248
            ArrayVTable::builder()
×
249
                .as_ptr(array_as_ptr::<T, N>)
×
250
                .as_mut_ptr(array_as_mut_ptr::<T, N>)
×
251
                .build()
×
252
        }
×
253

254
        ShapeBuilder::for_sized::<[T; N]>("[T; N]")
255
            .type_name(array_type_name)
256
            .ty(Type::Sequence(crate::SequenceType::Array(
257
                crate::ArrayType { t: T::SHAPE, n: N },
258
            )))
259
            .def(Def::Array(ArrayDef::new(
260
                &const { build_array_vtable::<T, N>() },
261
                T::SHAPE,
262
                N,
263
            )))
264
            .type_params(&[TypeParam {
265
                name: "T",
266
                shape: T::SHAPE,
267
            }])
268
            .vtable_indirect(&ARRAY_VTABLE)
269
            .type_ops_indirect(
270
                &const {
NEW
271
                    unsafe fn truthy<const N: usize>(_: PtrConst) -> bool {
×
NEW
272
                        N != 0
×
NEW
273
                    }
×
274

275
                    TypeOpsIndirect {
276
                        drop_in_place: array_drop,
277
                        default_in_place: Some(array_default),
278
                        clone_into: Some(array_clone),
279
                        is_truthy: Some(truthy::<N>),
280
                    }
281
                },
282
            )
283
            .build()
284
    };
285
}
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