facet_json/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2#![warn(missing_docs)]
3#![warn(clippy::std_instead_of_core)]
4#![warn(clippy::std_instead_of_alloc)]
5#![forbid(unsafe_code)]
6#![doc = include_str!("../README.md")]
7
8#[cfg(feature = "std")]
9use std::io::{self, Write};
10
11use facet_core::Facet;
12pub use facet_deserialize::{DeserError, DeserErrorKind, DeserErrorMessage};
13use facet_reflect::Peek;
14
15extern crate alloc;
16
17#[cfg(feature = "std")]
18mod iterative;
19#[cfg(feature = "std")]
20mod recursive;
21mod tokenizer;
22
23const MAX_RECURSION_DEPTH: usize = 100;
24
25/// Deserialize JSON from a given byte slice
26#[cfg(feature = "std")]
27pub fn from_slice<'input: 'facet, 'facet, T: Facet<'facet>>(
28    input: &'input [u8],
29) -> Result<T, DeserError<'input>> {
30    recursive::from_slice(input, 0)
31}
32
33/// Deserialize JSON from a given string
34#[cfg(feature = "std")]
35pub fn from_str<'input: 'facet, 'facet, T: Facet<'facet>>(
36    input: &'input str,
37) -> Result<T, DeserError<'input>> {
38    recursive::from_str(input, 0)
39}
40
41/// Deserialize JSON from a given string, converting any dynamic error into a static one.
42///
43/// This function attempts to deserialize a type `T` implementing `Facet` from the input string slice.
44/// If deserialization fails, the error is converted into an owned, static error type to avoid lifetime issues.
45#[cfg(feature = "std")]
46pub fn from_str_static_error<'input: 'facet, 'facet, T: Facet<'facet>>(
47    input: &'input str,
48) -> Result<T, DeserError<'input>> {
49    recursive::from_str_static_error(input, 0)
50}
51
52/// Serializes a value to JSON
53#[cfg(feature = "std")]
54pub fn to_string<'facet, T: Facet<'facet>>(value: &T) -> String {
55    recursive::to_string(value, 0)
56}
57
58/// Serializes a Peek instance to JSON
59#[cfg(feature = "std")]
60pub fn peek_to_string<'facet: 'input, 'input: 'facet>(peek: Peek<'input, 'facet>) -> String {
61    recursive::peek_to_string(peek, 0)
62}
63
64/// Serializes a value to a writer in JSON format
65#[cfg(feature = "std")]
66pub fn to_writer<'mem: 'facet, 'facet, T: Facet<'facet>, W: Write>(
67    value: &'mem T,
68    writer: &mut W,
69) -> io::Result<()> {
70    recursive::to_writer(value, writer)
71}
72
73/// Serializes a Peek instance to a writer in JSON format
74#[cfg(feature = "std")]
75pub fn peek_to_writer<'mem: 'facet, 'facet, W: Write>(
76    peek: Peek<'mem, 'facet>,
77    writer: &mut W,
78) -> io::Result<()> {
79    recursive::peek_to_writer(peek, None, 0, writer)
80}
81
82/// The JSON format
83struct Json;
84
85/// Properly escapes and writes a JSON string
86#[cfg(feature = "std")]
87fn write_json_string<W: Write>(writer: &mut W, s: &str) -> io::Result<()> {
88    writer.write_all(b"\"")?;
89
90    for c in s.chars() {
91        write_json_escaped_char(writer, c)?;
92    }
93
94    writer.write_all(b"\"")
95}
96
97/// Writes a single JSON escaped character
98#[cfg(feature = "std")]
99fn write_json_escaped_char<W: Write>(writer: &mut W, c: char) -> io::Result<()> {
100    match c {
101        '"' => writer.write_all(b"\\\""),
102        '\\' => writer.write_all(b"\\\\"),
103        '\n' => writer.write_all(b"\\n"),
104        '\r' => writer.write_all(b"\\r"),
105        '\t' => writer.write_all(b"\\t"),
106        '\u{08}' => writer.write_all(b"\\b"),
107        '\u{0C}' => writer.write_all(b"\\f"),
108        c if c.is_control() => {
109            let mut buf = [0; 6];
110            let s = format!("{:04x}", c as u32);
111            buf[0] = b'\\';
112            buf[1] = b'u';
113            buf[2] = s.as_bytes()[0];
114            buf[3] = s.as_bytes()[1];
115            buf[4] = s.as_bytes()[2];
116            buf[5] = s.as_bytes()[3];
117            writer.write_all(&buf)
118        }
119        c => {
120            let mut buf = [0; 4];
121            let len = c.encode_utf8(&mut buf).len();
122            writer.write_all(&buf[..len])
123        }
124    }
125}
126
127fn variant_is_newtype_like(variant: &facet_core::Variant) -> bool {
128    variant.data.kind == facet_core::StructKind::Tuple && variant.data.fields.len() == 1
129}