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

facet-rs / facet / 19774894729

28 Nov 2025 10:22PM UTC coverage: 59.711% (-0.6%) from 60.346%
19774894729

push

github

fasterthanlime
Add DynamicValue support for deserializing into facet_value::Value

This adds support for deserializing JSON (and potentially other formats)
into facet_value::Value without format crates needing to depend on facet-value.

Key changes:
- Add Def::DynamicValue variant with vtable for building dynamic values
- Implement Facet trait for Value in facet-value
- Extend Partial to handle DynamicValue scalars via set_into_dynamic_value
- Add deserialize_dynamic_value handler in facet-json

Currently supports scalar values (null, bool, numbers, strings).
Arrays and objects are stubbed out with TODO errors.

451 of 922 new or added lines in 20 files covered. (48.92%)

29 existing lines in 3 files now uncovered.

16657 of 27896 relevant lines covered (59.71%)

153.76 hits per line

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

26.88
/facet-value/src/facet_impl.rs
1
//! Facet implementation for Value, enabling deserialization from any format.
2

3
use core::ptr::NonNull;
4

5
use facet_core::{
6
    ConstTypeId, Def, DynValueKind, DynamicValueDef, DynamicValueVTable, Facet, MarkerTraits,
7
    PtrConst, PtrMut, PtrUninit, Shape, ShapeLayout, Type, TypeNameOpts, UserType, ValueVTable,
8
};
9

10
use crate::{VArray, VBytes, VNumber, VObject, VString, Value};
11

12
// ============================================================================
13
// Scalar setters
14
// ============================================================================
15

NEW
16
unsafe fn dyn_set_null(dst: PtrUninit<'_>) {
×
NEW
17
    unsafe {
×
NEW
18
        let ptr = dst.as_mut_byte_ptr() as *mut Value;
×
NEW
19
        ptr.write(Value::NULL);
×
NEW
20
    }
×
NEW
21
}
×
22

23
unsafe fn dyn_set_bool(dst: PtrUninit<'_>, value: bool) {
3✔
24
    unsafe {
3✔
25
        let ptr = dst.as_mut_byte_ptr() as *mut Value;
3✔
26
        ptr.write(Value::from(value));
3✔
27
    }
3✔
28
}
3✔
29

30
unsafe fn dyn_set_i64(dst: PtrUninit<'_>, value: i64) {
11✔
31
    unsafe {
11✔
32
        let ptr = dst.as_mut_byte_ptr() as *mut Value;
11✔
33
        ptr.write(VNumber::from_i64(value).into_value());
11✔
34
    }
11✔
35
}
11✔
36

NEW
37
unsafe fn dyn_set_u64(dst: PtrUninit<'_>, value: u64) {
×
NEW
38
    unsafe {
×
NEW
39
        let ptr = dst.as_mut_byte_ptr() as *mut Value;
×
NEW
40
        ptr.write(VNumber::from_u64(value).into_value());
×
NEW
41
    }
×
NEW
42
}
×
43

44
unsafe fn dyn_set_f64(dst: PtrUninit<'_>, value: f64) -> bool {
1✔
45
    unsafe {
46
        let ptr = dst.as_mut_byte_ptr() as *mut Value;
1✔
47
        match VNumber::from_f64(value) {
1✔
48
            Some(num) => {
1✔
49
                ptr.write(num.into_value());
1✔
50
                true
1✔
51
            }
52
            None => {
53
                // NaN or infinity - write null as fallback and return false
NEW
54
                ptr.write(Value::NULL);
×
NEW
55
                false
×
56
            }
57
        }
58
    }
59
}
1✔
60

61
unsafe fn dyn_set_str(dst: PtrUninit<'_>, value: &str) {
9✔
62
    unsafe {
9✔
63
        let ptr = dst.as_mut_byte_ptr() as *mut Value;
9✔
64
        ptr.write(VString::new(value).into_value());
9✔
65
    }
9✔
66
}
9✔
67

NEW
68
unsafe fn dyn_set_bytes(dst: PtrUninit<'_>, value: &[u8]) {
×
NEW
69
    unsafe {
×
NEW
70
        let ptr = dst.as_mut_byte_ptr() as *mut Value;
×
NEW
71
        ptr.write(VBytes::new(value).into_value());
×
NEW
72
    }
×
NEW
73
}
×
74

75
// ============================================================================
76
// Array operations
77
// ============================================================================
78

79
unsafe fn dyn_begin_array(dst: PtrUninit<'_>) {
14✔
80
    unsafe {
14✔
81
        let ptr = dst.as_mut_byte_ptr() as *mut Value;
14✔
82
        ptr.write(VArray::new().into_value());
14✔
83
    }
14✔
84
}
14✔
85

86
unsafe fn dyn_push_array_element(array: PtrMut<'_>, element: PtrMut<'_>) {
18✔
87
    unsafe {
88
        let array_ptr = array.as_mut_byte_ptr() as *mut Value;
18✔
89
        let element_ptr = element.as_mut_byte_ptr() as *mut Value;
18✔
90

91
        // Read the element (moving it out)
92
        let element_value = element_ptr.read();
18✔
93

94
        // Get the array and push
95
        let array_value = &mut *array_ptr;
18✔
96
        if let Some(arr) = array_value.as_array_mut() {
18✔
97
            arr.push(element_value);
18✔
98
        }
18✔
99
    }
100
}
18✔
101

102
// ============================================================================
103
// Object operations
104
// ============================================================================
105

NEW
106
unsafe fn dyn_begin_object(dst: PtrUninit<'_>) {
×
NEW
107
    unsafe {
×
NEW
108
        let ptr = dst.as_mut_byte_ptr() as *mut Value;
×
NEW
109
        ptr.write(VObject::new().into_value());
×
NEW
110
    }
×
NEW
111
}
×
112

NEW
113
unsafe fn dyn_insert_object_entry(object: PtrMut<'_>, key: &str, value: PtrMut<'_>) {
×
114
    unsafe {
NEW
115
        let object_ptr = object.as_mut_byte_ptr() as *mut Value;
×
NEW
116
        let value_ptr = value.as_mut_byte_ptr() as *mut Value;
×
117

118
        // Read the value (moving it out)
NEW
119
        let entry_value = value_ptr.read();
×
120

121
        // Get the object and insert
NEW
122
        let object_value = &mut *object_ptr;
×
NEW
123
        if let Some(obj) = object_value.as_object_mut() {
×
NEW
124
            obj.insert(key, entry_value);
×
NEW
125
        }
×
126
    }
NEW
127
}
×
128

129
// ============================================================================
130
// Read operations
131
// ============================================================================
132

NEW
133
unsafe fn dyn_get_kind(value: PtrConst<'_>) -> DynValueKind {
×
134
    unsafe {
NEW
135
        let ptr = value.as_byte_ptr() as *const Value;
×
NEW
136
        let v = &*ptr;
×
NEW
137
        match v.value_type() {
×
NEW
138
            crate::ValueType::Null => DynValueKind::Null,
×
NEW
139
            crate::ValueType::Bool => DynValueKind::Bool,
×
NEW
140
            crate::ValueType::Number => DynValueKind::Number,
×
NEW
141
            crate::ValueType::String => DynValueKind::String,
×
NEW
142
            crate::ValueType::Bytes => DynValueKind::Bytes,
×
NEW
143
            crate::ValueType::Array => DynValueKind::Array,
×
NEW
144
            crate::ValueType::Object => DynValueKind::Object,
×
145
        }
146
    }
NEW
147
}
×
148

NEW
149
unsafe fn dyn_get_bool(value: PtrConst<'_>) -> Option<bool> {
×
150
    unsafe {
NEW
151
        let ptr = value.as_byte_ptr() as *const Value;
×
NEW
152
        (*ptr).as_bool()
×
153
    }
NEW
154
}
×
155

NEW
156
unsafe fn dyn_get_i64(value: PtrConst<'_>) -> Option<i64> {
×
157
    unsafe {
NEW
158
        let ptr = value.as_byte_ptr() as *const Value;
×
NEW
159
        (*ptr).as_number().and_then(|n| n.to_i64())
×
160
    }
NEW
161
}
×
162

NEW
163
unsafe fn dyn_get_u64(value: PtrConst<'_>) -> Option<u64> {
×
164
    unsafe {
NEW
165
        let ptr = value.as_byte_ptr() as *const Value;
×
NEW
166
        (*ptr).as_number().and_then(|n| n.to_u64())
×
167
    }
NEW
168
}
×
169

NEW
170
unsafe fn dyn_get_f64(value: PtrConst<'_>) -> Option<f64> {
×
171
    unsafe {
NEW
172
        let ptr = value.as_byte_ptr() as *const Value;
×
NEW
173
        (*ptr).as_number().map(|n| n.to_f64_lossy())
×
174
    }
NEW
175
}
×
176

NEW
177
unsafe fn dyn_get_str<'a>(value: PtrConst<'a>) -> Option<&'a str> {
×
178
    unsafe {
NEW
179
        let ptr = value.as_byte_ptr() as *const Value;
×
NEW
180
        (*ptr).as_string().map(|s| s.as_str())
×
181
    }
NEW
182
}
×
183

NEW
184
unsafe fn dyn_get_bytes<'a>(value: PtrConst<'a>) -> Option<&'a [u8]> {
×
185
    unsafe {
NEW
186
        let ptr = value.as_byte_ptr() as *const Value;
×
NEW
187
        (*ptr).as_bytes().map(|b| b.as_slice())
×
188
    }
NEW
189
}
×
190

NEW
191
unsafe fn dyn_array_len(value: PtrConst<'_>) -> Option<usize> {
×
192
    unsafe {
NEW
193
        let ptr = value.as_byte_ptr() as *const Value;
×
NEW
194
        (*ptr).as_array().map(|a| a.len())
×
195
    }
NEW
196
}
×
197

NEW
198
unsafe fn dyn_array_get(value: PtrConst<'_>, index: usize) -> Option<PtrConst<'_>> {
×
199
    unsafe {
NEW
200
        let ptr = value.as_byte_ptr() as *const Value;
×
NEW
201
        (*ptr).as_array().and_then(|a| {
×
NEW
202
            a.get(index)
×
NEW
203
                .map(|elem| PtrConst::new(NonNull::new_unchecked(elem as *const Value as *mut u8)))
×
NEW
204
        })
×
205
    }
NEW
206
}
×
207

NEW
208
unsafe fn dyn_object_len(value: PtrConst<'_>) -> Option<usize> {
×
209
    unsafe {
NEW
210
        let ptr = value.as_byte_ptr() as *const Value;
×
NEW
211
        (*ptr).as_object().map(|o| o.len())
×
212
    }
NEW
213
}
×
214

NEW
215
unsafe fn dyn_object_get_entry<'a>(
×
NEW
216
    value: PtrConst<'a>,
×
NEW
217
    index: usize,
×
NEW
218
) -> Option<(&'a str, PtrConst<'a>)> {
×
219
    unsafe {
NEW
220
        let ptr = value.as_byte_ptr() as *const Value;
×
NEW
221
        (*ptr).as_object().and_then(|o| {
×
NEW
222
            o.iter().nth(index).map(|(k, v)| {
×
NEW
223
                (
×
NEW
224
                    k.as_str(),
×
NEW
225
                    PtrConst::new(NonNull::new_unchecked(v as *const Value as *mut u8)),
×
NEW
226
                )
×
NEW
227
            })
×
NEW
228
        })
×
229
    }
NEW
230
}
×
231

NEW
232
unsafe fn dyn_object_get<'a>(value: PtrConst<'a>, key: &str) -> Option<PtrConst<'a>> {
×
233
    unsafe {
NEW
234
        let ptr = value.as_byte_ptr() as *const Value;
×
NEW
235
        (*ptr).as_object().and_then(|o| {
×
NEW
236
            o.get(key)
×
NEW
237
                .map(|v| PtrConst::new(NonNull::new_unchecked(v as *const Value as *mut u8)))
×
NEW
238
        })
×
239
    }
NEW
240
}
×
241

242
// ============================================================================
243
// VTable and Shape
244
// ============================================================================
245

246
static DYNAMIC_VALUE_VTABLE: DynamicValueVTable = DynamicValueVTable::builder()
247
    .set_null(dyn_set_null)
248
    .set_bool(dyn_set_bool)
249
    .set_i64(dyn_set_i64)
250
    .set_u64(dyn_set_u64)
251
    .set_f64(dyn_set_f64)
252
    .set_str(dyn_set_str)
253
    .set_bytes(dyn_set_bytes)
254
    .begin_array(dyn_begin_array)
255
    .push_array_element(dyn_push_array_element)
256
    .begin_object(dyn_begin_object)
257
    .insert_object_entry(dyn_insert_object_entry)
258
    .get_kind(dyn_get_kind)
259
    .get_bool(dyn_get_bool)
260
    .get_i64(dyn_get_i64)
261
    .get_u64(dyn_get_u64)
262
    .get_f64(dyn_get_f64)
263
    .get_str(dyn_get_str)
264
    .get_bytes(dyn_get_bytes)
265
    .array_len(dyn_array_len)
266
    .array_get(dyn_array_get)
267
    .object_len(dyn_object_len)
268
    .object_get_entry(dyn_object_get_entry)
269
    .object_get(dyn_object_get)
270
    .build();
271

272
static DYNAMIC_VALUE_DEF: DynamicValueDef = DynamicValueDef::builder()
273
    .vtable(&DYNAMIC_VALUE_VTABLE)
274
    .build();
275

276
// Value vtable functions for the standard Facet machinery
277

278
unsafe fn value_drop_in_place(value: PtrMut<'_>) -> PtrUninit<'_> {
14✔
279
    unsafe {
280
        let ptr = value.as_mut_byte_ptr() as *mut Value;
14✔
281
        core::ptr::drop_in_place(ptr);
14✔
282
        PtrUninit::new(NonNull::new_unchecked(ptr as *mut u8))
14✔
283
    }
284
}
14✔
285

NEW
286
unsafe fn value_clone_into<'src, 'dst>(src: PtrConst<'src>, dst: PtrUninit<'dst>) -> PtrMut<'dst> {
×
287
    unsafe {
NEW
288
        let src_ptr = src.as_byte_ptr() as *const Value;
×
NEW
289
        let dst_ptr = dst.as_mut_byte_ptr() as *mut Value;
×
NEW
290
        dst_ptr.write((*src_ptr).clone());
×
NEW
291
        PtrMut::new(NonNull::new_unchecked(dst_ptr as *mut u8))
×
292
    }
NEW
293
}
×
294

NEW
295
unsafe fn value_debug(value: PtrConst<'_>, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
×
296
    unsafe {
NEW
297
        let ptr = value.as_byte_ptr() as *const Value;
×
NEW
298
        core::fmt::Debug::fmt(&*ptr, f)
×
299
    }
NEW
300
}
×
301

302
unsafe fn value_default_in_place(dst: PtrUninit<'_>) -> PtrMut<'_> {
6✔
303
    unsafe {
304
        let ptr = dst.as_mut_byte_ptr() as *mut Value;
6✔
305
        ptr.write(Value::default());
6✔
306
        PtrMut::new(NonNull::new_unchecked(ptr as *mut u8))
6✔
307
    }
308
}
6✔
309

NEW
310
unsafe fn value_partial_eq(a: PtrConst<'_>, b: PtrConst<'_>) -> bool {
×
311
    unsafe {
NEW
312
        let a_ptr = a.as_byte_ptr() as *const Value;
×
NEW
313
        let b_ptr = b.as_byte_ptr() as *const Value;
×
NEW
314
        *a_ptr == *b_ptr
×
315
    }
NEW
316
}
×
317

318
/// Wrapper to allow hashing through a `&mut dyn Hasher`
319
struct HasherWrapper<'a>(&'a mut dyn core::hash::Hasher);
320

321
impl core::hash::Hasher for HasherWrapper<'_> {
NEW
322
    fn finish(&self) -> u64 {
×
NEW
323
        self.0.finish()
×
NEW
324
    }
×
NEW
325
    fn write(&mut self, bytes: &[u8]) {
×
NEW
326
        self.0.write(bytes)
×
NEW
327
    }
×
328
}
329

NEW
330
unsafe fn value_hash(value: PtrConst<'_>, hasher: &mut dyn core::hash::Hasher) {
×
331
    unsafe {
332
        use core::hash::Hash;
NEW
333
        let ptr = value.as_byte_ptr() as *const Value;
×
NEW
334
        let mut wrapper = HasherWrapper(hasher);
×
NEW
335
        (*ptr).hash(&mut wrapper);
×
336
    }
NEW
337
}
×
338

NEW
339
fn value_type_name(f: &mut core::fmt::Formatter<'_>, _opts: TypeNameOpts) -> core::fmt::Result {
×
NEW
340
    write!(f, "Value")
×
NEW
341
}
×
342

343
static VALUE_VTABLE: ValueVTable = ValueVTable {
344
    type_name: value_type_name,
345
    marker_traits: MarkerTraits::SEND.union(MarkerTraits::SYNC),
346
    drop_in_place: Some(value_drop_in_place),
347
    invariants: None,
348
    display: None,
349
    debug: Some(value_debug),
350
    default_in_place: Some(value_default_in_place),
351
    clone_into: Some(value_clone_into),
352
    partial_eq: Some(value_partial_eq),
353
    partial_ord: None,
354
    ord: None,
355
    hash: Some(value_hash),
356
    parse: None,
357
    try_from: None,
358
    try_into_inner: None,
359
    try_borrow_inner: None,
360
};
361

362
/// The static shape for `Value`.
363
pub static VALUE_SHAPE: Shape = Shape {
364
    id: ConstTypeId::of::<Value>(),
365
    layout: ShapeLayout::Sized(core::alloc::Layout::new::<Value>()),
366
    vtable: VALUE_VTABLE,
367
    ty: Type::User(UserType::Opaque),
368
    def: Def::DynamicValue(DYNAMIC_VALUE_DEF),
369
    type_identifier: "Value",
370
    type_params: &[],
371
    doc: &[" A dynamic value that can hold null, bool, number, string, bytes, array, or object."],
372
    attributes: &[],
373
    type_tag: None,
374
    inner: None,
375
};
376

377
unsafe impl Facet<'_> for Value {
378
    const SHAPE: &'static Shape = &VALUE_SHAPE;
379
}
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