Skip to main content

facet_value/
serialize.rs

1//! Serialize any type implementing `Facet` into a `Value`.
2//!
3//! This module provides the inverse of [`from_value`](crate::from_value): given a Rust type
4//! that implements `Facet`, you can serialize it into a dynamic `Value`.
5//!
6//! # Example
7//!
8//! ```
9//! use facet::Facet;
10//! use facet_value::{Value, to_value, from_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//!
20//! // Serialize to a Value
21//! let value = to_value(&person).unwrap();
22//!
23//! // The value can be inspected dynamically
24//! assert!(value.is_object());
25//! let obj = value.as_object().unwrap();
26//! assert_eq!(obj.get("name").unwrap().as_string().unwrap().as_str(), "Alice");
27//! assert_eq!(obj.get("age").unwrap().as_number().unwrap().to_u64(), Some(30));
28//!
29//! // Or deserialized back to the original type
30//! let person2: Person = from_value(value).unwrap();
31//! assert_eq!(person, person2);
32//! ```
33
34use alloc::string::String;
35use alloc::vec::Vec;
36
37use facet_core::Facet;
38use facet_format::{FormatSerializer, ScalarValue, SerializeError, serialize_root};
39use facet_reflect::Peek;
40
41use crate::{VArray, VNumber, VObject, VString, Value};
42
43/// Error type for Value serialization.
44#[derive(Debug)]
45pub struct ToValueError {
46    msg: String,
47}
48
49impl ToValueError {
50    /// Create a new error with the given message.
51    pub fn new(msg: impl Into<String>) -> Self {
52        Self { msg: msg.into() }
53    }
54}
55
56impl core::fmt::Display for ToValueError {
57    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
58        f.write_str(&self.msg)
59    }
60}
61
62#[cfg(feature = "std")]
63impl core::error::Error for ToValueError {}
64
65/// Serializer that builds a `Value` from a sequence of format events.
66struct ValueSerializer {
67    /// Stack of containers being built
68    stack: Vec<StackFrame>,
69    /// The final result
70    result: Option<Value>,
71}
72
73/// A frame on the serialization stack, representing a container being built.
74enum StackFrame {
75    /// Building an object
76    Object {
77        obj: VObject,
78        pending_key: Option<String>,
79    },
80    /// Building an array
81    Array { arr: VArray },
82}
83
84impl ValueSerializer {
85    fn new() -> Self {
86        Self {
87            stack: Vec::new(),
88            result: None,
89        }
90    }
91
92    fn finish(self) -> Value {
93        self.result.unwrap_or(Value::NULL)
94    }
95
96    fn emit(&mut self, value: Value) {
97        match self.stack.last_mut() {
98            Some(StackFrame::Object { obj, pending_key }) => {
99                if let Some(key) = pending_key.take() {
100                    obj.insert(key, value);
101                } else {
102                    // This shouldn't happen in well-formed input
103                    panic!("emit called on object without pending key");
104                }
105            }
106            Some(StackFrame::Array { arr }) => {
107                arr.push(value);
108            }
109            None => {
110                self.result = Some(value);
111            }
112        }
113    }
114}
115
116impl FormatSerializer for ValueSerializer {
117    type Error = ToValueError;
118
119    fn begin_struct(&mut self) -> Result<(), Self::Error> {
120        self.stack.push(StackFrame::Object {
121            obj: VObject::new(),
122            pending_key: None,
123        });
124        Ok(())
125    }
126
127    fn field_key(&mut self, key: &str) -> Result<(), Self::Error> {
128        match self.stack.last_mut() {
129            Some(StackFrame::Object { pending_key, .. }) => {
130                *pending_key = Some(key.to_string());
131                Ok(())
132            }
133            _ => Err(ToValueError::new("field_key called outside of object")),
134        }
135    }
136
137    fn end_struct(&mut self) -> Result<(), Self::Error> {
138        match self.stack.pop() {
139            Some(StackFrame::Object { obj, .. }) => {
140                self.emit(obj.into());
141                Ok(())
142            }
143            _ => Err(ToValueError::new(
144                "end_struct called without matching begin_struct",
145            )),
146        }
147    }
148
149    fn begin_seq(&mut self) -> Result<(), Self::Error> {
150        self.stack.push(StackFrame::Array { arr: VArray::new() });
151        Ok(())
152    }
153
154    fn end_seq(&mut self) -> Result<(), Self::Error> {
155        match self.stack.pop() {
156            Some(StackFrame::Array { arr }) => {
157                self.emit(arr.into());
158                Ok(())
159            }
160            _ => Err(ToValueError::new(
161                "end_seq called without matching begin_seq",
162            )),
163        }
164    }
165
166    fn scalar(&mut self, scalar: ScalarValue<'_>) -> Result<(), Self::Error> {
167        let value = match scalar {
168            ScalarValue::Unit | ScalarValue::Null => Value::NULL,
169            ScalarValue::Bool(b) => Value::from(b),
170            ScalarValue::Char(c) => VString::new(&c.to_string()).into(),
171            ScalarValue::I64(n) => VNumber::from_i64(n).into(),
172            ScalarValue::U64(n) => VNumber::from_u64(n).into(),
173            ScalarValue::I128(n) => {
174                // Store as string for large numbers
175                VString::new(&n.to_string()).into()
176            }
177            ScalarValue::U128(n) => {
178                // Store as string for large numbers
179                VString::new(&n.to_string()).into()
180            }
181            ScalarValue::F64(n) => VNumber::from_f64(n).map(Into::into).unwrap_or(Value::NULL),
182            ScalarValue::Str(s) => VString::new(&s).into(),
183            ScalarValue::Bytes(b) => crate::VBytes::new(b.as_ref()).into(),
184        };
185        self.emit(value);
186        Ok(())
187    }
188}
189
190/// Serialize a value implementing `Facet` into a `Value`.
191///
192/// This is the main entry point for converting a typed Rust value into a
193/// dynamic `Value` that can be inspected, modified, or serialized to various formats.
194///
195/// # Example
196///
197/// ```
198/// use facet::Facet;
199/// use facet_value::to_value;
200///
201/// #[derive(Facet)]
202/// struct Point {
203///     x: i32,
204///     y: i32,
205/// }
206///
207/// let point = Point { x: 10, y: 20 };
208/// let value = to_value(&point).unwrap();
209///
210/// // Access fields dynamically
211/// let obj = value.as_object().unwrap();
212/// assert_eq!(obj.get("x").unwrap().as_number().unwrap().to_i64(), Some(10));
213/// assert_eq!(obj.get("y").unwrap().as_number().unwrap().to_i64(), Some(20));
214/// ```
215pub fn to_value<'facet, T: Facet<'facet>>(
216    value: &T,
217) -> Result<Value, SerializeError<ToValueError>> {
218    let mut serializer = ValueSerializer::new();
219    serialize_root(&mut serializer, Peek::new(value))?;
220    Ok(serializer.finish())
221}
222
223/// Serialize a `Peek` instance into a `Value`.
224///
225/// This allows serializing values without requiring ownership, useful when
226/// you already have a `Peek` from reflection operations.
227///
228/// # Example
229///
230/// ```
231/// use facet::Facet;
232/// use facet_reflect::Peek;
233/// use facet_value::peek_to_value;
234///
235/// #[derive(Facet)]
236/// struct Point { x: i32, y: i32 }
237///
238/// let point = Point { x: 10, y: 20 };
239/// let value = peek_to_value(Peek::new(&point)).unwrap();
240///
241/// assert!(value.is_object());
242/// ```
243pub fn peek_to_value<'mem, 'facet>(
244    peek: Peek<'mem, 'facet>,
245) -> Result<Value, SerializeError<ToValueError>> {
246    let mut serializer = ValueSerializer::new();
247    serialize_root(&mut serializer, peek)?;
248    Ok(serializer.finish())
249}
250
251#[cfg(test)]
252mod tests {
253    use super::*;
254    use alloc::collections::BTreeMap;
255    use alloc::string::ToString;
256    use alloc::vec;
257
258    #[test]
259    fn test_to_value_primitives() {
260        // bool
261        let v = to_value(&true).unwrap();
262        assert_eq!(v.as_bool(), Some(true));
263
264        let v = to_value(&false).unwrap();
265        assert_eq!(v.as_bool(), Some(false));
266
267        // integers
268        let v = to_value(&42i32).unwrap();
269        assert_eq!(v.as_number().unwrap().to_i64(), Some(42));
270
271        let v = to_value(&123u64).unwrap();
272        assert_eq!(v.as_number().unwrap().to_u64(), Some(123));
273
274        // float
275        let v = to_value(&2.5f64).unwrap();
276        assert!((v.as_number().unwrap().to_f64().unwrap() - 2.5).abs() < 0.001);
277
278        // string
279        let s = "hello".to_string();
280        let v = to_value(&s).unwrap();
281        assert_eq!(v.as_string().unwrap().as_str(), "hello");
282    }
283
284    #[test]
285    fn test_to_value_option() {
286        let some: Option<i32> = Some(42);
287        let v = to_value(&some).unwrap();
288        assert_eq!(v.as_number().unwrap().to_i64(), Some(42));
289
290        let none: Option<i32> = None;
291        let v = to_value(&none).unwrap();
292        assert!(v.is_null());
293    }
294
295    #[test]
296    fn test_to_value_vec() {
297        let vec = vec![1i32, 2, 3];
298        let v = to_value(&vec).unwrap();
299
300        let arr = v.as_array().unwrap();
301        assert_eq!(arr.len(), 3);
302        assert_eq!(arr.get(0).unwrap().as_number().unwrap().to_i64(), Some(1));
303        assert_eq!(arr.get(1).unwrap().as_number().unwrap().to_i64(), Some(2));
304        assert_eq!(arr.get(2).unwrap().as_number().unwrap().to_i64(), Some(3));
305    }
306
307    #[test]
308    fn test_to_value_map() {
309        let mut map: BTreeMap<String, i32> = BTreeMap::new();
310        map.insert("a".to_string(), 1);
311        map.insert("b".to_string(), 2);
312
313        let v = to_value(&map).unwrap();
314
315        let obj = v.as_object().unwrap();
316        assert_eq!(obj.get("a").unwrap().as_number().unwrap().to_i64(), Some(1));
317        assert_eq!(obj.get("b").unwrap().as_number().unwrap().to_i64(), Some(2));
318    }
319
320    #[test]
321    fn test_to_value_nested() {
322        // Vec<Option<i32>>
323        let vec = vec![Some(1i32), None, Some(3)];
324        let v = to_value(&vec).unwrap();
325
326        let arr = v.as_array().unwrap();
327        assert_eq!(arr.len(), 3);
328        assert_eq!(arr.get(0).unwrap().as_number().unwrap().to_i64(), Some(1));
329        assert!(arr.get(1).unwrap().is_null());
330        assert_eq!(arr.get(2).unwrap().as_number().unwrap().to_i64(), Some(3));
331    }
332
333    #[test]
334    fn test_roundtrip_value() {
335        // Roundtrip: Value -> Value should be identity
336        let original = crate::value!({
337            "name": "Alice",
338            "age": 30,
339            "active": true
340        });
341
342        let v = to_value(&original).unwrap();
343        assert_eq!(v, original);
344    }
345}