Skip to main content

facet_json/
lib.rs

1// Note: unsafe code is used for lifetime transmutes in from_slice_into/from_str_into
2// when BORROW=false, mirroring the approach used in facet-format's FormatDeserializer.
3
4//! JSON parser and serializer using facet-format.
5//!
6//! This crate provides JSON support via the `FormatParser` trait.
7
8extern crate alloc;
9
10/// Trace-level logging macro that forwards to `tracing::trace!` when the `tracing` feature is enabled.
11#[cfg(feature = "tracing")]
12#[allow(unused_macros)]
13macro_rules! trace {
14    ($($arg:tt)*) => {
15        ::tracing::trace!($($arg)*)
16    };
17}
18
19/// Trace-level logging macro (no-op when `tracing` feature is disabled).
20#[cfg(not(feature = "tracing"))]
21#[allow(unused_macros)]
22macro_rules! trace {
23    ($($arg:tt)*) => {};
24}
25
26/// Debug-level logging macro that forwards to `tracing::debug!` when the `tracing` feature is enabled.
27#[cfg(feature = "tracing")]
28#[allow(unused_macros)]
29macro_rules! debug {
30    ($($arg:tt)*) => {
31        ::tracing::debug!($($arg)*)
32    };
33}
34
35/// Debug-level logging macro (no-op when `tracing` feature is disabled).
36#[cfg(not(feature = "tracing"))]
37#[allow(unused_macros)]
38macro_rules! debug {
39    ($($arg:tt)*) => {};
40}
41
42#[allow(unused_imports)]
43pub(crate) use debug;
44use facet_reflect::Partial;
45#[allow(unused_imports)]
46pub(crate) use trace;
47
48mod error;
49mod parser;
50mod raw_json;
51mod scanner;
52mod serializer;
53
54#[cfg(feature = "axum")]
55mod axum;
56
57#[cfg(feature = "axum")]
58pub use axum::{Json, JsonRejection};
59
60pub use error::JsonError;
61pub use parser::JsonParser;
62pub use raw_json::RawJson;
63pub use serializer::{
64    BytesFormat, HexBytesOptions, JsonSerializeError, JsonSerializer, SerializeOptions,
65    peek_to_string, peek_to_string_pretty, peek_to_string_with_options, peek_to_writer_std,
66    peek_to_writer_std_pretty, peek_to_writer_std_with_options, to_string, to_string_pretty,
67    to_string_with_options, to_vec, to_vec_pretty, to_vec_with_options, to_writer_std,
68    to_writer_std_pretty, to_writer_std_with_options,
69};
70
71// Re-export DeserializeError for convenience
72pub use facet_format::DeserializeError;
73
74/// Deserialize a value from a JSON string into an owned type.
75///
76/// This is the recommended default for most use cases. The input does not need
77/// to outlive the result, making it suitable for deserializing from temporary
78/// buffers (e.g., HTTP request bodies).
79///
80/// Types containing `&str` fields cannot be deserialized with this function;
81/// use `String` or `Cow<str>` instead. For zero-copy deserialization into
82/// borrowed types, use [`from_str_borrowed`].
83///
84/// # Example
85///
86/// ```
87/// use facet::Facet;
88/// use facet_json::from_str;
89///
90/// #[derive(Facet, Debug, PartialEq)]
91/// struct Person {
92///     name: String,
93///     age: u32,
94/// }
95///
96/// let json = r#"{"name": "Alice", "age": 30}"#;
97/// let person: Person = from_str(json).unwrap();
98/// assert_eq!(person.name, "Alice");
99/// assert_eq!(person.age, 30);
100/// ```
101pub fn from_str<T>(input: &str) -> Result<T, DeserializeError>
102where
103    T: facet_core::Facet<'static>,
104{
105    use facet_format::FormatDeserializer;
106    // TRUSTED_UTF8 = true: input came from &str, so it's valid UTF-8
107    let mut parser = JsonParser::<true>::new(input.as_bytes());
108    let mut de = FormatDeserializer::new_owned(&mut parser);
109    de.deserialize_root()
110}
111
112/// Deserialize a value from JSON bytes into an owned type.
113///
114/// This is the recommended default for most use cases. The input does not need
115/// to outlive the result, making it suitable for deserializing from temporary
116/// buffers (e.g., HTTP request bodies).
117///
118/// Types containing `&str` fields cannot be deserialized with this function;
119/// use `String` or `Cow<str>` instead. For zero-copy deserialization into
120/// borrowed types, use [`from_slice_borrowed`].
121///
122/// # Example
123///
124/// ```
125/// use facet::Facet;
126/// use facet_json::from_slice;
127///
128/// #[derive(Facet, Debug, PartialEq)]
129/// struct Point {
130///     x: i32,
131///     y: i32,
132/// }
133///
134/// let json = br#"{"x": 10, "y": 20}"#;
135/// let point: Point = from_slice(json).unwrap();
136/// assert_eq!(point.x, 10);
137/// assert_eq!(point.y, 20);
138/// ```
139pub fn from_slice<T>(input: &[u8]) -> Result<T, DeserializeError>
140where
141    T: facet_core::Facet<'static>,
142{
143    use facet_format::FormatDeserializer;
144    let mut parser = JsonParser::<false>::new(input);
145    let mut de = FormatDeserializer::new_owned(&mut parser);
146    de.deserialize_root()
147}
148
149/// Deserialize a value from a JSON string, allowing zero-copy borrowing.
150///
151/// This variant requires the input to outlive the result (`'input: 'facet`),
152/// enabling zero-copy deserialization of string fields as `&str` or `Cow<str>`.
153///
154/// Use this when you need maximum performance and can guarantee the input
155/// buffer outlives the deserialized value. For most use cases, prefer
156/// [`from_str`] which doesn't have lifetime requirements.
157///
158/// # Example
159///
160/// ```
161/// use facet::Facet;
162/// use facet_json::from_str_borrowed;
163///
164/// #[derive(Facet, Debug, PartialEq)]
165/// struct Person<'a> {
166///     name: &'a str,
167///     age: u32,
168/// }
169///
170/// let json = r#"{"name": "Alice", "age": 30}"#;
171/// let person: Person = from_str_borrowed(json).unwrap();
172/// assert_eq!(person.name, "Alice");
173/// assert_eq!(person.age, 30);
174/// ```
175pub fn from_str_borrowed<'input, 'facet, T>(input: &'input str) -> Result<T, DeserializeError>
176where
177    T: facet_core::Facet<'facet>,
178    'input: 'facet,
179{
180    use facet_format::FormatDeserializer;
181    // TRUSTED_UTF8 = true: input came from &str, so it's valid UTF-8
182    let mut parser = JsonParser::<true>::new(input.as_bytes());
183    let mut de = FormatDeserializer::new(&mut parser);
184    de.deserialize_root()
185}
186
187/// Deserialize a value from JSON bytes, allowing zero-copy borrowing.
188///
189/// This variant requires the input to outlive the result (`'input: 'facet`),
190/// enabling zero-copy deserialization of string fields as `&str` or `Cow<str>`.
191///
192/// Use this when you need maximum performance and can guarantee the input
193/// buffer outlives the deserialized value. For most use cases, prefer
194/// [`from_slice`] which doesn't have lifetime requirements.
195///
196/// # Example
197///
198/// ```
199/// use facet::Facet;
200/// use facet_json::from_slice_borrowed;
201///
202/// #[derive(Facet, Debug, PartialEq)]
203/// struct Point<'a> {
204///     label: &'a str,
205///     x: i32,
206///     y: i32,
207/// }
208///
209/// let json = br#"{"label": "origin", "x": 0, "y": 0}"#;
210/// let point: Point = from_slice_borrowed(json).unwrap();
211/// assert_eq!(point.label, "origin");
212/// ```
213pub fn from_slice_borrowed<'input, 'facet, T>(input: &'input [u8]) -> Result<T, DeserializeError>
214where
215    T: facet_core::Facet<'facet>,
216    'input: 'facet,
217{
218    use facet_format::FormatDeserializer;
219    let mut parser = JsonParser::<false>::new(input);
220    let mut de = FormatDeserializer::new(&mut parser);
221    de.deserialize_root()
222}
223
224/// Deserialize JSON from a string into an existing Partial.
225///
226/// This is useful for reflection-based deserialization where you don't have
227/// a concrete type `T` at compile time, only its Shape metadata. The Partial
228/// must already be allocated for the target type.
229///
230/// This version produces owned strings (no borrowing from input).
231///
232/// # Example
233///
234/// ```
235/// use facet::Facet;
236/// use facet_json::from_str_into;
237/// use facet_reflect::Partial;
238///
239/// #[derive(Facet, Debug, PartialEq)]
240/// struct Person {
241///     name: String,
242///     age: u32,
243/// }
244///
245/// let json = r#"{"name": "Alice", "age": 30}"#;
246/// let partial = Partial::alloc_owned::<Person>().unwrap();
247/// let partial = from_str_into(json, partial).unwrap();
248/// let value = partial.build().unwrap();
249/// let person: Person = value.materialize().unwrap();
250/// assert_eq!(person.name, "Alice");
251/// assert_eq!(person.age, 30);
252/// ```
253pub fn from_str_into<'facet>(
254    input: &str,
255    partial: Partial<'facet, false>,
256) -> Result<Partial<'facet, false>, DeserializeError> {
257    use facet_format::{FormatDeserializer, MetaSource};
258    // TRUSTED_UTF8 = true: input came from &str, so it's valid UTF-8
259    let mut parser = JsonParser::<true>::new(input.as_bytes());
260    let mut de = FormatDeserializer::new_owned(&mut parser);
261
262    // SAFETY: The deserializer expects Partial<'input, false> where 'input is the
263    // lifetime of the JSON bytes. Since BORROW=false, no data is borrowed from the
264    // input, so the actual 'facet lifetime of the Partial is independent of 'input.
265    // We transmute to satisfy the type system, then transmute back after deserialization.
266    #[allow(unsafe_code)]
267    let partial: Partial<'_, false> =
268        unsafe { core::mem::transmute::<Partial<'facet, false>, Partial<'_, false>>(partial) };
269
270    let partial = de.deserialize_into(partial, MetaSource::FromEvents)?;
271
272    // SAFETY: Same reasoning - no borrowed data since BORROW=false.
273    #[allow(unsafe_code)]
274    let partial: Partial<'facet, false> =
275        unsafe { core::mem::transmute::<Partial<'_, false>, Partial<'facet, false>>(partial) };
276
277    Ok(partial)
278}
279
280/// Deserialize JSON from bytes into an existing Partial.
281///
282/// This is useful for reflection-based deserialization where you don't have
283/// a concrete type `T` at compile time, only its Shape metadata. The Partial
284/// must already be allocated for the target type.
285///
286/// This version produces owned strings (no borrowing from input).
287///
288/// # Example
289///
290/// ```
291/// use facet::Facet;
292/// use facet_json::from_slice_into;
293/// use facet_reflect::Partial;
294///
295/// #[derive(Facet, Debug, PartialEq)]
296/// struct Point {
297///     x: i32,
298///     y: i32,
299/// }
300///
301/// let json = br#"{"x": 10, "y": 20}"#;
302/// let partial = Partial::alloc_owned::<Point>().unwrap();
303/// let partial = from_slice_into(json, partial).unwrap();
304/// let value = partial.build().unwrap();
305/// let point: Point = value.materialize().unwrap();
306/// assert_eq!(point.x, 10);
307/// assert_eq!(point.y, 20);
308/// ```
309pub fn from_slice_into<'facet>(
310    input: &[u8],
311    partial: Partial<'facet, false>,
312) -> Result<Partial<'facet, false>, DeserializeError> {
313    use facet_format::{FormatDeserializer, MetaSource};
314    let mut parser = JsonParser::<false>::new(input);
315    let mut de = FormatDeserializer::new_owned(&mut parser);
316
317    // SAFETY: The deserializer expects Partial<'input, false> where 'input is the
318    // lifetime of the JSON bytes. Since BORROW=false, no data is borrowed from the
319    // input, so the actual 'facet lifetime of the Partial is independent of 'input.
320    // We transmute to satisfy the type system, then transmute back after deserialization.
321    #[allow(unsafe_code)]
322    let partial: Partial<'_, false> =
323        unsafe { core::mem::transmute::<Partial<'facet, false>, Partial<'_, false>>(partial) };
324
325    let partial = de.deserialize_into(partial, MetaSource::FromEvents)?;
326
327    // SAFETY: Same reasoning - no borrowed data since BORROW=false.
328    #[allow(unsafe_code)]
329    let partial: Partial<'facet, false> =
330        unsafe { core::mem::transmute::<Partial<'_, false>, Partial<'facet, false>>(partial) };
331
332    Ok(partial)
333}
334
335/// Deserialize JSON from a string into an existing Partial, allowing zero-copy borrowing.
336///
337/// This variant requires the input to outlive the Partial's lifetime (`'input: 'facet`),
338/// enabling zero-copy deserialization of string fields as `&str` or `Cow<str>`.
339///
340/// This is useful for reflection-based deserialization where you don't have
341/// a concrete type `T` at compile time, only its Shape metadata.
342///
343/// # Example
344///
345/// ```
346/// use facet::Facet;
347/// use facet_json::from_str_into_borrowed;
348/// use facet_reflect::Partial;
349///
350/// #[derive(Facet, Debug, PartialEq)]
351/// struct Person<'a> {
352///     name: &'a str,
353///     age: u32,
354/// }
355///
356/// let json = r#"{"name": "Alice", "age": 30}"#;
357/// let partial = Partial::alloc::<Person>().unwrap();
358/// let partial = from_str_into_borrowed(json, partial).unwrap();
359/// let value = partial.build().unwrap();
360/// let person: Person = value.materialize().unwrap();
361/// assert_eq!(person.name, "Alice");
362/// assert_eq!(person.age, 30);
363/// ```
364pub fn from_str_into_borrowed<'input, 'facet>(
365    input: &'input str,
366    partial: Partial<'facet, true>,
367) -> Result<Partial<'facet, true>, DeserializeError>
368where
369    'input: 'facet,
370{
371    use facet_format::{FormatDeserializer, MetaSource};
372    // TRUSTED_UTF8 = true: input came from &str, so it's valid UTF-8
373    let mut parser = JsonParser::<true>::new(input.as_bytes());
374    let mut de = FormatDeserializer::new(&mut parser);
375    de.deserialize_into(partial, MetaSource::FromEvents)
376}
377
378/// Deserialize JSON from bytes into an existing Partial, allowing zero-copy borrowing.
379///
380/// This variant requires the input to outlive the Partial's lifetime (`'input: 'facet`),
381/// enabling zero-copy deserialization of string fields as `&str` or `Cow<str>`.
382///
383/// This is useful for reflection-based deserialization where you don't have
384/// a concrete type `T` at compile time, only its Shape metadata.
385///
386/// # Example
387///
388/// ```
389/// use facet::Facet;
390/// use facet_json::from_slice_into_borrowed;
391/// use facet_reflect::Partial;
392///
393/// #[derive(Facet, Debug, PartialEq)]
394/// struct Point<'a> {
395///     label: &'a str,
396///     x: i32,
397///     y: i32,
398/// }
399///
400/// let json = br#"{"label": "origin", "x": 0, "y": 0}"#;
401/// let partial = Partial::alloc::<Point>().unwrap();
402/// let partial = from_slice_into_borrowed(json, partial).unwrap();
403/// let value = partial.build().unwrap();
404/// let point: Point = value.materialize().unwrap();
405/// assert_eq!(point.label, "origin");
406/// ```
407pub fn from_slice_into_borrowed<'input, 'facet>(
408    input: &'input [u8],
409    partial: Partial<'facet, true>,
410) -> Result<Partial<'facet, true>, DeserializeError>
411where
412    'input: 'facet,
413{
414    use facet_format::{FormatDeserializer, MetaSource};
415    let mut parser = JsonParser::<false>::new(input);
416    let mut de = FormatDeserializer::new(&mut parser);
417    de.deserialize_into(partial, MetaSource::FromEvents)
418}