Skip to main content

facet_postcard/
lib.rs

1//! Postcard binary format for facet.
2//!
3//! This crate provides serialization and deserialization for the postcard binary format.
4//!
5//! # Serialization
6//!
7//! Serialization supports all types that implement [`facet_core::Facet`]:
8//!
9//! ```
10//! use facet::Facet;
11//! use facet_postcard::to_vec;
12//!
13//! #[derive(Facet)]
14//! struct Point { x: i32, y: i32 }
15//!
16//! let point = Point { x: 10, y: 20 };
17//! let bytes = to_vec(&point).unwrap();
18//! ```
19//!
20//! # Deserialization
21//!
22//! There is a configurable [`Deserializer`] API plus convenience functions:
23//!
24//! - [`from_slice`]: Deserializes into owned types (`T: Facet<'static>`)
25//! - [`from_slice_borrowed`]: Deserializes with zero-copy borrowing from the input buffer
26//! - [`from_slice_with_shape`]: Deserializes into `Value` using runtime shape information
27//! - [`from_slice_into`]: Deserializes into an existing `Partial` (type-erased, owned)
28//! - [`from_slice_into_borrowed`]: Deserializes into an existing `Partial` (type-erased, zero-copy)
29//!
30//! ```
31//! use facet_postcard::from_slice;
32//!
33//! // Postcard encoding: [length=3, true, false, true]
34//! let bytes = &[0x03, 0x01, 0x00, 0x01];
35//! let result: Vec<bool> = from_slice(bytes).unwrap();
36//! assert_eq!(result, vec![true, false, true]);
37//! ```
38//!
39//! Both functions automatically select the best deserialization tier:
40//! - **Tier-2 (Format JIT)**: Fastest path for compatible types (primitives, structs, vecs, simple enums)
41//! - **Tier-0 (Reflection)**: Fallback for all other types (nested enums, complex types)
42//!
43//! This ensures all `Facet` types can be deserialized.
44
45// Note: unsafe code is used for lifetime transmutes in from_slice_into
46// when BORROW=false, mirroring the approach used in facet-json.
47
48extern crate alloc;
49
50mod error;
51mod parser;
52mod raw_postcard;
53mod serialize;
54mod shape_deser;
55
56#[cfg(feature = "axum")]
57mod axum;
58
59#[cfg(feature = "axum")]
60pub use axum::{Postcard, PostcardRejection, PostcardSerializeRejection};
61pub use error::{PostcardError, SerializeError};
62pub use parser::PostcardParser;
63pub use raw_postcard::{RawPostcard, opaque_encoded_borrowed, opaque_encoded_owned};
64pub use serialize::{
65    ScatterPlan, Segment, Writer, peek_to_scatter_plan, peek_to_vec, to_scatter_plan, to_vec,
66    to_vec_with_shape, to_writer_fallible,
67};
68pub use shape_deser::from_slice_with_shape;
69
70// Re-export DeserializeError for convenience
71pub use facet_format::DeserializeError;
72
73/// Default maximum number of elements allowed in a decoded collection.
74///
75/// This limit applies to postcard length-prefixed collections (lists, maps,
76/// dynamic arrays/objects) and is enforced in both Tier-0 and Tier-2 JIT paths.
77pub const DEFAULT_MAX_COLLECTION_ELEMENTS: u64 = 1 << 24; // 16,777,216
78
79/// Deserialization safety/configuration options.
80#[derive(Debug, Clone, Copy, PartialEq, Eq)]
81pub struct DeserializeConfig {
82    max_collection_elements: u64,
83}
84
85impl Default for DeserializeConfig {
86    fn default() -> Self {
87        Self {
88            max_collection_elements: DEFAULT_MAX_COLLECTION_ELEMENTS,
89        }
90    }
91}
92
93impl DeserializeConfig {
94    /// Create default deserialization settings.
95    pub const fn new() -> Self {
96        Self {
97            max_collection_elements: DEFAULT_MAX_COLLECTION_ELEMENTS,
98        }
99    }
100
101    /// Set the maximum number of elements permitted in any decoded collection.
102    pub const fn max_collection_elements(mut self, max_collection_elements: u64) -> Self {
103        self.max_collection_elements = max_collection_elements;
104        self
105    }
106
107    /// Get the configured maximum number of collection elements.
108    pub const fn get_max_collection_elements(self) -> u64 {
109        self.max_collection_elements
110    }
111}
112
113/// Builder-style postcard deserializer.
114///
115/// This single API supports all current entry points:
116/// typed owned/borrowed deserialization, shape-based value deserialization,
117/// and deserialization into existing `Partial` values.
118#[derive(Debug, Clone, Copy)]
119pub struct Deserializer<'input> {
120    input: &'input [u8],
121    config: DeserializeConfig,
122}
123
124impl<'input> Deserializer<'input> {
125    /// Create a deserializer for a postcard byte slice with default settings.
126    pub const fn new(input: &'input [u8]) -> Self {
127        Self {
128            input,
129            config: DeserializeConfig::new(),
130        }
131    }
132
133    /// Create a deserializer with explicit settings.
134    pub const fn with_config(input: &'input [u8], config: DeserializeConfig) -> Self {
135        Self { input, config }
136    }
137
138    /// Replace all deserialization settings.
139    pub const fn config(mut self, config: DeserializeConfig) -> Self {
140        self.config = config;
141        self
142    }
143
144    /// Configure the maximum collection element count.
145    pub const fn max_collection_elements(mut self, max_collection_elements: u64) -> Self {
146        self.config = self.config.max_collection_elements(max_collection_elements);
147        self
148    }
149
150    fn parser(self) -> PostcardParser<'input> {
151        PostcardParser::with_limits(self.input, self.config.get_max_collection_elements())
152    }
153
154    /// Deserialize into an owned typed value.
155    pub fn deserialize<T>(self) -> Result<T, DeserializeError>
156    where
157        T: facet_core::Facet<'static>,
158    {
159        use facet_format::FormatDeserializer;
160        let mut parser = self.parser();
161        let mut de = FormatDeserializer::new_owned(&mut parser);
162        de.deserialize()
163    }
164
165    /// Deserialize into a borrowed typed value.
166    pub fn deserialize_borrowed<'facet, T>(self) -> Result<T, DeserializeError>
167    where
168        T: facet_core::Facet<'facet>,
169        'input: 'facet,
170    {
171        use facet_format::FormatDeserializer;
172        let mut parser = self.parser();
173        let mut de = FormatDeserializer::new(&mut parser);
174        de.deserialize()
175    }
176
177    /// Deserialize into a dynamic `Value` using a runtime shape.
178    pub fn deserialize_with_shape(
179        self,
180        source_shape: &'static facet_core::Shape,
181    ) -> Result<facet_value::Value, DeserializeError> {
182        use facet_format::FormatDeserializer;
183        let mut parser = self.parser();
184        let mut de = FormatDeserializer::new_owned(&mut parser);
185        de.deserialize_with_shape(source_shape)
186    }
187
188    /// Deserialize into an existing owned `Partial`.
189    pub fn deserialize_into<'facet>(
190        self,
191        partial: facet_reflect::Partial<'facet, false>,
192    ) -> Result<facet_reflect::Partial<'facet, false>, DeserializeError> {
193        use facet_format::{FormatDeserializer, MetaSource};
194        let mut parser = self.parser();
195        let mut de = FormatDeserializer::new_owned(&mut parser);
196
197        #[allow(unsafe_code)]
198        let partial: facet_reflect::Partial<'_, false> = unsafe {
199            core::mem::transmute::<
200                facet_reflect::Partial<'facet, false>,
201                facet_reflect::Partial<'_, false>,
202            >(partial)
203        };
204
205        let partial = de.deserialize_into(partial, MetaSource::FromEvents)?;
206
207        #[allow(unsafe_code)]
208        let partial: facet_reflect::Partial<'facet, false> = unsafe {
209            core::mem::transmute::<
210                facet_reflect::Partial<'_, false>,
211                facet_reflect::Partial<'facet, false>,
212            >(partial)
213        };
214
215        Ok(partial)
216    }
217
218    /// Deserialize into an existing borrowed `Partial`.
219    pub fn deserialize_into_borrowed<'facet>(
220        self,
221        partial: facet_reflect::Partial<'facet, true>,
222    ) -> Result<facet_reflect::Partial<'facet, true>, DeserializeError>
223    where
224        'input: 'facet,
225    {
226        use facet_format::{FormatDeserializer, MetaSource};
227        let mut parser = self.parser();
228        let mut de = FormatDeserializer::new(&mut parser);
229        de.deserialize_into(partial, MetaSource::FromEvents)
230    }
231}
232
233/// Deserialize a value from postcard bytes into an owned type.
234///
235/// This is the recommended default for most use cases. The input does not need
236/// to outlive the result, making it suitable for deserializing from temporary
237/// buffers (e.g., HTTP request bodies).
238///
239/// Types containing `&str` or `&[u8]` fields cannot be deserialized with this
240/// function; use `String`/`Vec<u8>` or `Cow<str>`/`Cow<[u8]>` instead. For
241/// zero-copy deserialization into borrowed types, use [`from_slice_borrowed`].
242///
243/// # Example
244///
245/// ```
246/// use facet::Facet;
247/// use facet_postcard::from_slice;
248///
249/// #[derive(Facet, Debug, PartialEq)]
250/// struct Point {
251///     x: i32,
252///     y: i32,
253/// }
254///
255/// // Postcard encoding: [x=10 (zigzag), y=20 (zigzag)]
256/// let bytes = &[0x14, 0x28];
257/// let point: Point = from_slice(bytes).unwrap();
258/// assert_eq!(point.x, 10);
259/// assert_eq!(point.y, 20);
260/// ```
261pub fn from_slice<T>(input: &[u8]) -> Result<T, DeserializeError>
262where
263    T: facet_core::Facet<'static>,
264{
265    Deserializer::new(input).deserialize()
266}
267
268/// Deserialize a value from postcard bytes, allowing zero-copy borrowing.
269///
270/// This variant requires the input to outlive the result (`'input: 'facet`),
271/// enabling zero-copy deserialization of byte slices as `&[u8]` or `Cow<[u8]>`.
272///
273/// Use this when you need maximum performance and can guarantee the input
274/// buffer outlives the deserialized value. For most use cases, prefer
275/// [`from_slice`] which doesn't have lifetime requirements.
276///
277/// # Example
278///
279/// ```
280/// use facet::Facet;
281/// use facet_postcard::from_slice_borrowed;
282///
283/// #[derive(Facet, Debug, PartialEq)]
284/// struct Message<'a> {
285///     id: u32,
286///     data: &'a [u8],
287/// }
288///
289/// // Postcard encoding: [id=1, data_len=3, 0xAB, 0xCD, 0xEF]
290/// let bytes = &[0x01, 0x03, 0xAB, 0xCD, 0xEF];
291/// let msg: Message = from_slice_borrowed(bytes).unwrap();
292/// assert_eq!(msg.id, 1);
293/// assert_eq!(msg.data, &[0xAB, 0xCD, 0xEF]);
294/// ```
295pub fn from_slice_borrowed<'input, 'facet, T>(input: &'input [u8]) -> Result<T, DeserializeError>
296where
297    T: facet_core::Facet<'facet>,
298    'input: 'facet,
299{
300    Deserializer::new(input).deserialize_borrowed()
301}
302
303/// Deserialize postcard bytes into an existing Partial.
304///
305/// This is useful for reflection-based deserialization where you don't have
306/// a concrete type `T` at compile time, only its Shape metadata. The Partial
307/// must already be allocated for the target type.
308///
309/// This version produces owned strings (no borrowing from input).
310///
311/// # Example
312///
313/// ```
314/// use facet::Facet;
315/// use facet_postcard::from_slice_into;
316/// use facet_reflect::Partial;
317///
318/// #[derive(Facet, Debug, PartialEq)]
319/// struct Point {
320///     x: i32,
321///     y: i32,
322/// }
323///
324/// // Postcard encoding: [x=10 (zigzag), y=20 (zigzag)]
325/// let bytes = &[0x14, 0x28];
326/// let partial = Partial::alloc_owned::<Point>().unwrap();
327/// let partial = from_slice_into(bytes, partial).unwrap();
328/// let value = partial.build().unwrap();
329/// let point: Point = value.materialize().unwrap();
330/// assert_eq!(point.x, 10);
331/// assert_eq!(point.y, 20);
332/// ```
333pub fn from_slice_into<'facet>(
334    input: &[u8],
335    partial: facet_reflect::Partial<'facet, false>,
336) -> Result<facet_reflect::Partial<'facet, false>, DeserializeError> {
337    Deserializer::new(input).deserialize_into(partial)
338}
339
340/// Deserialize postcard bytes into an existing Partial, allowing zero-copy borrowing.
341///
342/// This variant requires the input to outlive the Partial's lifetime (`'input: 'facet`),
343/// enabling zero-copy deserialization of byte slices as `&[u8]` or `Cow<[u8]>`.
344///
345/// This is useful for reflection-based deserialization where you don't have
346/// a concrete type `T` at compile time, only its Shape metadata.
347///
348/// # Example
349///
350/// ```
351/// use facet::Facet;
352/// use facet_postcard::from_slice_into_borrowed;
353/// use facet_reflect::Partial;
354///
355/// #[derive(Facet, Debug, PartialEq)]
356/// struct Message<'a> {
357///     id: u32,
358///     data: &'a [u8],
359/// }
360///
361/// // Postcard encoding: [id=1, data_len=3, 0xAB, 0xCD, 0xEF]
362/// let bytes = &[0x01, 0x03, 0xAB, 0xCD, 0xEF];
363/// let partial = Partial::alloc::<Message>().unwrap();
364/// let partial = from_slice_into_borrowed(bytes, partial).unwrap();
365/// let value = partial.build().unwrap();
366/// let msg: Message = value.materialize().unwrap();
367/// assert_eq!(msg.id, 1);
368/// assert_eq!(msg.data, &[0xAB, 0xCD, 0xEF]);
369/// ```
370pub fn from_slice_into_borrowed<'input, 'facet>(
371    input: &'input [u8],
372    partial: facet_reflect::Partial<'facet, true>,
373) -> Result<facet_reflect::Partial<'facet, true>, DeserializeError>
374where
375    'input: 'facet,
376{
377    Deserializer::new(input).deserialize_into_borrowed(partial)
378}