serde_json_core/
str.rs

1//! Utilities for serializing and deserializing strings.
2
3use core::fmt;
4
5#[derive(Debug)]
6/// A fragment of an escaped string
7pub enum EscapedStringFragment<'a> {
8    /// A series of characters which weren't escaped in the input.
9    NotEscaped(&'a str),
10    /// A character which was escaped in the input.
11    Escaped(char),
12}
13
14#[derive(Debug)]
15/// Errors occuring while unescaping strings.
16pub enum StringUnescapeError {
17    /// Failed to unescape a character due to an invalid escape sequence.
18    InvalidEscapeSequence,
19}
20
21impl fmt::Display for StringUnescapeError {
22    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23        match self {
24            StringUnescapeError::InvalidEscapeSequence => write!(
25                f,
26                "Failed to unescape a character due to an invalid escape sequence."
27            ),
28        }
29    }
30}
31
32#[cfg(feature = "std")]
33impl std::error::Error for StringUnescapeError {}
34
35fn unescape_next_fragment(
36    escaped_string: &str,
37) -> Result<(EscapedStringFragment<'_>, &str), StringUnescapeError> {
38    Ok(if let Some(rest) = escaped_string.strip_prefix('\\') {
39        let mut escaped_string_chars = rest.chars();
40
41        let unescaped_char = match escaped_string_chars.next() {
42            Some('"') => '"',
43            Some('\\') => '\\',
44            Some('/') => '/',
45            Some('b') => '\x08',
46            Some('f') => '\x0C',
47            Some('n') => '\n',
48            Some('r') => '\r',
49            Some('t') => '\t',
50            Some('u') => {
51                fn split_first_slice(s: &str, len: usize) -> Option<(&str, &str)> {
52                    Some((s.get(..len)?, s.get(len..)?))
53                }
54
55                let (escape_sequence, remaining_escaped_string_chars) =
56                    split_first_slice(escaped_string_chars.as_str(), 4)
57                        .ok_or(StringUnescapeError::InvalidEscapeSequence)?;
58
59                escaped_string_chars = remaining_escaped_string_chars.chars();
60
61                u32::from_str_radix(escape_sequence, 16)
62                    .ok()
63                    .and_then(char::from_u32)
64                    .ok_or(StringUnescapeError::InvalidEscapeSequence)?
65            }
66            _ => return Err(StringUnescapeError::InvalidEscapeSequence),
67        };
68
69        (
70            EscapedStringFragment::Escaped(unescaped_char),
71            escaped_string_chars.as_str(),
72        )
73    } else {
74        let (fragment, rest) =
75            escaped_string.split_at(escaped_string.find('\\').unwrap_or(escaped_string.len()));
76
77        (EscapedStringFragment::NotEscaped(fragment), rest)
78    })
79}
80
81/// A borrowed escaped string. `EscapedStr` can be used to borrow an escaped string from the input,
82/// even when deserialized using `from_str_escaped` or `from_slice_escaped`.
83///
84/// ```
85///     #[derive(serde::Deserialize)]
86///     struct Event<'a> {
87///         name: heapless::String<16>,
88///         #[serde(borrow)]
89///         description: serde_json_core::str::EscapedStr<'a>,
90///     }
91///     
92///     serde_json_core::de::from_str_escaped::<Event<'_>>(
93///         r#"{ "name": "Party\u0021", "description": "I'm throwing a party! Hopefully the \u2600 shines!" }"#,
94///         &mut [0; 8],
95///     )
96///     .unwrap();
97/// ```
98#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
99#[serde(rename = "__serde_json_core_escaped_string__")]
100pub struct EscapedStr<'a>(pub &'a str);
101
102impl<'a> EscapedStr<'a> {
103    pub(crate) const NAME: &'static str = "__serde_json_core_escaped_string__";
104
105    /// Returns an iterator over the `EscapedStringFragment`s of an escaped string.
106    pub fn fragments(&self) -> EscapedStringFragmentIter<'a> {
107        EscapedStringFragmentIter(self.0)
108    }
109}
110
111/// An iterator over the `EscapedStringFragment`s of an escaped string.
112pub struct EscapedStringFragmentIter<'a>(&'a str);
113
114impl<'a> EscapedStringFragmentIter<'a> {
115    /// Views the underlying data as a subslice of the original data.
116    pub fn as_str(&self) -> EscapedStr<'a> {
117        EscapedStr(self.0)
118    }
119}
120
121impl<'a> Iterator for EscapedStringFragmentIter<'a> {
122    type Item = Result<EscapedStringFragment<'a>, StringUnescapeError>;
123
124    fn next(&mut self) -> Option<Self::Item> {
125        if self.0.is_empty() {
126            return None;
127        }
128
129        Some(unescape_next_fragment(self.0).map(|(fragment, rest)| {
130            self.0 = rest;
131
132            fragment
133        }))
134    }
135}