Skip to main content

serde_json_core/
str.rs

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