Skip to main content

facet_value/
serialize.rs

1//! Serialize any type implementing `Facet` into a [`Value`].
2//!
3//! This crate hosts the adapter between `facet-format`'s event serializer model
4//! and `facet-value`'s dynamic `Value` type.
5//!
6//! # Example
7//!
8//! ```
9//! use facet::Facet;
10//! use facet_value::{Value, from_value, to_value};
11//!
12//! #[derive(Debug, Facet, PartialEq)]
13//! struct Person {
14//!     name: String,
15//!     age: u32,
16//! }
17//!
18//! let person = Person { name: "Alice".into(), age: 30 };
19//! let value: Value = to_value(&person).unwrap();
20//!
21//! let person2: Person = from_value(value).unwrap();
22//! assert_eq!(person, person2);
23//! ```
24
25#![cfg_attr(not(feature = "std"), no_std)]
26#![warn(missing_docs)]
27
28extern crate alloc;
29
30use alloc::string::String;
31use alloc::vec::Vec;
32
33use crate::{VArray, VChar, VNumber, VObject, VString, Value};
34use facet_core::Facet;
35use facet_format::{FormatSerializer, ScalarValue, SerializeError, serialize_root};
36use facet_reflect::Peek;
37
38use crate::VBytes;
39
40/// Error type for `Value` serialization.
41#[derive(Debug)]
42pub struct ToValueError {
43    msg: String,
44}
45
46impl ToValueError {
47    /// Create a new error with the given message.
48    pub fn new(msg: impl Into<String>) -> Self {
49        Self { msg: msg.into() }
50    }
51}
52
53impl core::fmt::Display for ToValueError {
54    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
55        f.write_str(&self.msg)
56    }
57}
58
59impl core::error::Error for ToValueError {}
60
61/// Serializer that builds a [`Value`] from a sequence of format events.
62struct ValueSerializer {
63    stack: Vec<StackFrame>,
64    result: Option<Value>,
65}
66
67enum StackFrame {
68    Object {
69        obj: VObject,
70        pending_key: Option<String>,
71    },
72    Array {
73        arr: VArray,
74    },
75}
76
77impl ValueSerializer {
78    fn new() -> Self {
79        Self {
80            stack: Vec::new(),
81            result: None,
82        }
83    }
84
85    fn finish(self) -> Value {
86        self.result.unwrap_or(Value::NULL)
87    }
88
89    fn emit(&mut self, value: Value) {
90        match self.stack.last_mut() {
91            Some(StackFrame::Object { obj, pending_key }) => {
92                if let Some(key) = pending_key.take() {
93                    obj.insert(key, value);
94                } else {
95                    panic!("emit called on object without pending key");
96                }
97            }
98            Some(StackFrame::Array { arr }) => {
99                arr.push(value);
100            }
101            None => {
102                self.result = Some(value);
103            }
104        }
105    }
106}
107
108impl FormatSerializer for ValueSerializer {
109    type Error = ToValueError;
110
111    fn begin_struct(&mut self) -> Result<(), Self::Error> {
112        self.stack.push(StackFrame::Object {
113            obj: VObject::new(),
114            pending_key: None,
115        });
116        Ok(())
117    }
118
119    fn field_key(&mut self, key: &str) -> Result<(), Self::Error> {
120        match self.stack.last_mut() {
121            Some(StackFrame::Object { pending_key, .. }) => {
122                *pending_key = Some(key.to_string());
123                Ok(())
124            }
125            _ => Err(ToValueError::new("field_key called outside of object")),
126        }
127    }
128
129    fn end_struct(&mut self) -> Result<(), Self::Error> {
130        match self.stack.pop() {
131            Some(StackFrame::Object { obj, .. }) => {
132                self.emit(obj.into());
133                Ok(())
134            }
135            _ => Err(ToValueError::new(
136                "end_struct called without matching begin_struct",
137            )),
138        }
139    }
140
141    fn begin_seq(&mut self) -> Result<(), Self::Error> {
142        self.stack.push(StackFrame::Array { arr: VArray::new() });
143        Ok(())
144    }
145
146    fn end_seq(&mut self) -> Result<(), Self::Error> {
147        match self.stack.pop() {
148            Some(StackFrame::Array { arr }) => {
149                self.emit(arr.into());
150                Ok(())
151            }
152            _ => Err(ToValueError::new(
153                "end_seq called without matching begin_seq",
154            )),
155        }
156    }
157
158    fn scalar(&mut self, scalar: ScalarValue<'_>) -> Result<(), Self::Error> {
159        let value = match scalar {
160            ScalarValue::Unit | ScalarValue::Null => Value::NULL,
161            ScalarValue::Bool(b) => Value::from(b),
162            ScalarValue::Char(c) => VChar::new(c).into(),
163            ScalarValue::I64(n) => VNumber::from_i64(n).into(),
164            ScalarValue::U64(n) => VNumber::from_u64(n).into(),
165            ScalarValue::I128(n) => VNumber::from_i128(n).into(),
166            ScalarValue::U128(n) => VNumber::from_u128(n).into(),
167            ScalarValue::F64(n) => VNumber::from_f64(n).into(),
168            ScalarValue::Str(s) => VString::new(&s).into(),
169            ScalarValue::Bytes(b) => VBytes::new(b.as_ref()).into(),
170            _ => return Err(ToValueError::new("unsupported scalar value kind")),
171        };
172        self.emit(value);
173        Ok(())
174    }
175}
176
177/// Serialize a value implementing `Facet` into a [`Value`].
178pub fn to_value<'facet, T: Facet<'facet>>(
179    value: &T,
180) -> Result<Value, SerializeError<ToValueError>> {
181    let mut serializer = ValueSerializer::new();
182    serialize_root(&mut serializer, Peek::new(value))?;
183    Ok(serializer.finish())
184}
185
186/// Serialize a [`Peek`] instance into a [`Value`].
187pub fn peek_to_value<'mem, 'facet>(
188    peek: Peek<'mem, 'facet>,
189) -> Result<Value, SerializeError<ToValueError>> {
190    let mut serializer = ValueSerializer::new();
191    serialize_root(&mut serializer, peek)?;
192    Ok(serializer.finish())
193}
194
195#[cfg(test)]
196mod tests {
197    use super::*;
198    use alloc::collections::BTreeMap;
199    use alloc::string::ToString;
200    use alloc::vec;
201
202    #[test]
203    fn test_to_value_primitives() {
204        let v = to_value(&true).unwrap();
205        assert_eq!(v.as_bool(), Some(true));
206
207        let v = to_value(&false).unwrap();
208        assert_eq!(v.as_bool(), Some(false));
209
210        let v = to_value(&42i32).unwrap();
211        assert_eq!(v.as_number().unwrap().to_i64(), Some(42));
212
213        let v = to_value(&123u64).unwrap();
214        assert_eq!(v.as_number().unwrap().to_u64(), Some(123));
215
216        let v = to_value(&2.5f64).unwrap();
217        assert!((v.as_number().unwrap().to_f64().unwrap() - 2.5).abs() < 0.001);
218
219        let s = "hello".to_string();
220        let v = to_value(&s).unwrap();
221        assert_eq!(v.as_string().unwrap().as_str(), "hello");
222    }
223
224    #[test]
225    fn test_to_value_option() {
226        let some: Option<i32> = Some(42);
227        let v = to_value(&some).unwrap();
228        assert_eq!(v.as_number().unwrap().to_i64(), Some(42));
229
230        let none: Option<i32> = None;
231        let v = to_value(&none).unwrap();
232        assert!(v.is_null());
233    }
234
235    #[test]
236    fn test_to_value_vec() {
237        let vec = vec![1i32, 2, 3];
238        let v = to_value(&vec).unwrap();
239
240        let arr = v.as_array().unwrap();
241        assert_eq!(arr.len(), 3);
242        assert_eq!(arr.get(0).unwrap().as_number().unwrap().to_i64(), Some(1));
243        assert_eq!(arr.get(1).unwrap().as_number().unwrap().to_i64(), Some(2));
244        assert_eq!(arr.get(2).unwrap().as_number().unwrap().to_i64(), Some(3));
245    }
246
247    #[test]
248    fn test_to_value_map() {
249        let mut map: BTreeMap<String, i32> = BTreeMap::new();
250        map.insert("a".to_string(), 1);
251        map.insert("b".to_string(), 2);
252
253        let v = to_value(&map).unwrap();
254
255        let obj = v.as_object().unwrap();
256        assert_eq!(obj.get("a").unwrap().as_number().unwrap().to_i64(), Some(1));
257        assert_eq!(obj.get("b").unwrap().as_number().unwrap().to_i64(), Some(2));
258    }
259
260    #[test]
261    fn test_to_value_nested() {
262        let vec = vec![Some(1i32), None, Some(3)];
263        let v = to_value(&vec).unwrap();
264
265        let arr = v.as_array().unwrap();
266        assert_eq!(arr.len(), 3);
267        assert_eq!(arr.get(0).unwrap().as_number().unwrap().to_i64(), Some(1));
268        assert!(arr.get(1).unwrap().is_null());
269        assert_eq!(arr.get(2).unwrap().as_number().unwrap().to_i64(), Some(3));
270    }
271
272    #[test]
273    fn test_roundtrip_value() {
274        let original = crate::value!({
275            "name": "Alice",
276            "age": 30,
277            "active": true
278        });
279
280        let v = to_value(&original).unwrap();
281        assert_eq!(v, original);
282    }
283
284    #[test]
285    fn test_to_value_char() {
286        // char must be preserved as a Char value via the serializer mapping.
287        let c = 'λ';
288        let v = to_value(&c).unwrap();
289        assert_eq!(v.value_type(), crate::ValueType::Char);
290        assert_eq!(v.as_char(), Some('λ'));
291    }
292
293    #[test]
294    fn test_scalar_128_mapping_preserves_number() {
295        use facet_format::ScalarValue;
296
297        // When a serializer emits the 128-bit scalar variants directly, our
298        // mapping must build a Number (not stringify). (Note: facet-format's
299        // own serializer currently stringifies real u128/i128 values upstream;
300        // this exercises our mapping directly.)
301        let mut ser = ValueSerializer::new();
302        ser.scalar(ScalarValue::U128(u128::MAX)).unwrap();
303        let v = ser.finish();
304        assert_eq!(v.value_type(), crate::ValueType::Number);
305        assert_eq!(v.as_number().unwrap().to_u128(), Some(u128::MAX));
306
307        let mut ser = ValueSerializer::new();
308        ser.scalar(ScalarValue::I128(i128::MIN)).unwrap();
309        let v = ser.finish();
310        assert_eq!(v.value_type(), crate::ValueType::Number);
311        assert_eq!(v.as_number().unwrap().to_i128(), Some(i128::MIN));
312    }
313
314    #[test]
315    fn test_roundtrip_128_and_char_struct() {
316        use crate::from_value;
317        use facet::Facet;
318
319        #[derive(Debug, Facet, PartialEq)]
320        struct Wide {
321            big_u: u128,
322            big_i: i128,
323            letter: char,
324        }
325
326        let original = Wide {
327            big_u: u128::MAX,
328            big_i: i128::MIN,
329            letter: '🦀',
330        };
331
332        // char is preserved as a Char value in the intermediate representation.
333        let v = to_value(&original).unwrap();
334        let obj = v.as_object().unwrap();
335        assert_eq!(obj.get("letter").unwrap().as_char(), Some('🦀'));
336
337        // The full struct must round-trip intact through to_value / from_value,
338        // including the 128-bit fields.
339        let back: Wide = from_value(v).unwrap();
340        assert_eq!(back, original);
341    }
342}