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}