1use core::fmt;
4
5#[derive(Debug)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8pub enum EscapedStringFragment<'a> {
9 NotEscaped(&'a str),
11 Escaped(char),
13}
14
15#[derive(Debug)]
17#[cfg_attr(feature = "defmt", derive(defmt::Format))]
18pub enum StringUnescapeError {
19 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#[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 pub fn fragments(&self) -> EscapedStringFragmentIter<'a> {
110 EscapedStringFragmentIter(self.0)
111 }
112}
113
114pub struct EscapedStringFragmentIter<'a>(&'a str);
116
117impl<'a> EscapedStringFragmentIter<'a> {
118 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}