Skip to main content

facet_postcard/
shape_deser.rs

1//! Shape-based deserialization into `facet_value::Value`.
2//!
3//! This module provides deserialization from postcard bytes into a `Value` using only
4//! a `&'static Shape` at runtime, without requiring a concrete Rust type.
5
6use facet_core::Shape;
7use facet_format::{DeserializeError, FormatDeserializer};
8use facet_value::Value;
9
10use crate::error::PostcardError;
11use crate::parser::PostcardParser;
12
13/// Deserialize postcard bytes into a `Value` using shape information.
14///
15/// Since postcard is not a self-describing format, you need to provide the shape
16/// that describes the structure of the data.
17///
18/// # Example
19///
20/// ```
21/// use facet::Facet;
22/// use facet_postcard::{from_slice_with_shape, to_vec};
23///
24/// #[derive(Facet)]
25/// struct Point { x: i32, y: i32 }
26///
27/// let point = Point { x: 10, y: 20 };
28/// let bytes = to_vec(&point).unwrap();
29///
30/// // Deserialize using the shape, not the type
31/// let value = from_slice_with_shape(&bytes, Point::SHAPE).unwrap();
32/// let obj = value.as_object().unwrap();
33/// assert_eq!(obj.get("x").unwrap().as_number().unwrap().to_i64(), Some(10));
34/// assert_eq!(obj.get("y").unwrap().as_number().unwrap().to_i64(), Some(20));
35/// ```
36pub fn from_slice_with_shape(
37    input: &[u8],
38    source_shape: &'static Shape,
39) -> Result<Value, DeserializeError<PostcardError>> {
40    let parser = PostcardParser::new(input);
41    let mut de = FormatDeserializer::new_owned(parser);
42    de.deserialize_with_shape(source_shape)
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48    use facet::Facet;
49
50    #[test]
51    fn test_from_slice_with_shape_primitives() {
52        // Test i32
53        let bytes = crate::to_vec(&42i32).unwrap();
54        let value = from_slice_with_shape(&bytes, i32::SHAPE).unwrap();
55        assert_eq!(value.as_number().unwrap().to_i64(), Some(42));
56
57        // Test String
58        let bytes = crate::to_vec(&"hello".to_string()).unwrap();
59        let value = from_slice_with_shape(&bytes, String::SHAPE).unwrap();
60        assert_eq!(value.as_string().unwrap().as_str(), "hello");
61
62        // Test bool
63        let bytes = crate::to_vec(&true).unwrap();
64        let value = from_slice_with_shape(&bytes, bool::SHAPE).unwrap();
65        assert_eq!(value.as_bool(), Some(true));
66    }
67
68    #[test]
69    fn test_from_slice_with_shape_struct() {
70        #[derive(Facet)]
71        struct Point {
72            x: i32,
73            y: i32,
74        }
75
76        let point = Point { x: 10, y: 20 };
77        let bytes = crate::to_vec(&point).unwrap();
78        let value = from_slice_with_shape(&bytes, Point::SHAPE).unwrap();
79
80        let obj = value.as_object().unwrap();
81        assert_eq!(obj.get("x").unwrap().as_number().unwrap().to_i64(), Some(10));
82        assert_eq!(obj.get("y").unwrap().as_number().unwrap().to_i64(), Some(20));
83    }
84
85    #[test]
86    fn test_from_slice_with_shape_vec() {
87        let vec = vec![1i32, 2, 3, 4, 5];
88        let bytes = crate::to_vec(&vec).unwrap();
89        let value = from_slice_with_shape(&bytes, <Vec<i32>>::SHAPE).unwrap();
90
91        let arr = value.as_array().unwrap();
92        assert_eq!(arr.len(), 5);
93        assert_eq!(arr.get(0).unwrap().as_number().unwrap().to_i64(), Some(1));
94        assert_eq!(arr.get(4).unwrap().as_number().unwrap().to_i64(), Some(5));
95    }
96
97    #[test]
98    fn test_from_slice_with_shape_option() {
99        // Some value
100        let opt: Option<i32> = Some(42);
101        let bytes = crate::to_vec(&opt).unwrap();
102        let value = from_slice_with_shape(&bytes, <Option<i32>>::SHAPE).unwrap();
103        assert_eq!(value.as_number().unwrap().to_i64(), Some(42));
104
105        // None
106        let opt: Option<i32> = None;
107        let bytes = crate::to_vec(&opt).unwrap();
108        let value = from_slice_with_shape(&bytes, <Option<i32>>::SHAPE).unwrap();
109        assert!(value.is_null());
110    }
111
112    #[test]
113    fn test_from_slice_with_shape_enum() {
114        #[derive(Facet)]
115        #[repr(u8)]
116        #[allow(dead_code)]
117        enum Message {
118            Ping,
119            Text(String),
120            Point { x: i32, y: i32 },
121        }
122
123        // Unit variant
124        let msg = Message::Ping;
125        let bytes = crate::to_vec(&msg).unwrap();
126        let value = from_slice_with_shape(&bytes, Message::SHAPE).unwrap();
127        assert_eq!(value.as_string().unwrap().as_str(), "Ping");
128
129        // Newtype variant
130        let msg = Message::Text("hello".into());
131        let bytes = crate::to_vec(&msg).unwrap();
132        let value = from_slice_with_shape(&bytes, Message::SHAPE).unwrap();
133        let obj = value.as_object().unwrap();
134        assert_eq!(obj.get("Text").unwrap().as_string().unwrap().as_str(), "hello");
135
136        // Struct variant
137        let msg = Message::Point { x: 10, y: 20 };
138        let bytes = crate::to_vec(&msg).unwrap();
139        let value = from_slice_with_shape(&bytes, Message::SHAPE).unwrap();
140        let obj = value.as_object().unwrap();
141        let inner = obj.get("Point").unwrap().as_object().unwrap();
142        assert_eq!(inner.get("x").unwrap().as_number().unwrap().to_i64(), Some(10));
143        assert_eq!(inner.get("y").unwrap().as_number().unwrap().to_i64(), Some(20));
144    }
145}