Skip to content

Commit 7718c0b

Browse files
committed
fix escaping in serialization
Single quotes in single quoted strings were erroneously escaped to double quotes. Moreover, unicode line separators were not escaped, resulting in output that is an invalid document.
1 parent 1eba2a0 commit 7718c0b

File tree

1 file changed

+153
-1
lines changed

1 file changed

+153
-1
lines changed

src/utils.rs

Lines changed: 153 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ pub (crate) fn escape_double_quoted(input: &str) -> String {
5353
'/' => { escaped.push('\\'); escaped.push('/'); }
5454
'\u{0008}' => { escaped.push('\\'); escaped.push('b'); }
5555
'\u{000c}' => { escaped.push('\\'); escaped.push('f'); }
56+
'\u{2028}' => { escaped.push_str("\\u2028"); }
57+
'\u{2029}' => { escaped.push_str("\\u2029"); }
5658
_ => escaped.push(c),
5759
}
5860
}
@@ -65,14 +67,16 @@ pub (crate) fn escape_single_quoted(input: &str) -> String {
6567
let mut escaped = String::with_capacity(input.len() * 2);
6668
for c in input.chars() {
6769
match c {
68-
'\'' => { escaped.push('\\'); escaped.push('"'); }
70+
'\'' => { escaped.push('\\'); escaped.push('\''); }
6971
'\\' => { escaped.push('\\'); escaped.push('\\'); }
7072
'\n' => { escaped.push('\\'); escaped.push('n'); }
7173
'\r' => { escaped.push('\\'); escaped.push('r'); }
7274
'\t' => { escaped.push('\\'); escaped.push('t'); }
7375
'/' => { escaped.push('\\'); escaped.push('/'); }
7476
'\u{0008}' => { escaped.push('\\'); escaped.push('b'); }
7577
'\u{000c}' => { escaped.push('\\'); escaped.push('f'); }
78+
'\u{2028}' => { escaped.push_str("\\u2028"); }
79+
'\u{2029}' => { escaped.push_str("\\u2029"); }
7680
_ => escaped.push(c),
7781
}
7882
}
@@ -176,3 +180,151 @@ pub (crate) const MAX_DEPTH: usize = 2000;
176180

177181
#[cfg(feature = "unlimited_depth")]
178182
pub (crate) const MAX_DEPTH: usize = usize::MAX;
183+
184+
#[cfg(test)]
185+
mod tests {
186+
use super::*;
187+
188+
#[test]
189+
fn test_escape_single_quote_bug() {
190+
let input = "Hello'World";
191+
let result = escape_single_quoted(input);
192+
let expected = "Hello\\'World";
193+
assert_eq!(result, expected, "Single quote should be escaped as \\' not \\\"");
194+
}
195+
196+
#[test]
197+
fn test_escape_double_quoted_comprehensive() {
198+
let input = "Hello\"World\n\t\r\\";
199+
let result = escape_double_quoted(input);
200+
let expected = "Hello\\\"World\\n\\t\\r\\\\";
201+
assert_eq!(result, expected);
202+
}
203+
204+
#[test]
205+
fn test_escape_single_quoted_comprehensive() {
206+
let input = "Hello'World\n\t\r\\";
207+
let result = escape_single_quoted(input);
208+
let expected = "Hello\\'World\\n\\t\\r\\\\";
209+
assert_eq!(result, expected);
210+
}
211+
212+
#[test]
213+
fn test_unescape_basic_escapes() {
214+
let input = "Hello\\nWorld\\t\\r\\\\";
215+
let result = unescape(input).unwrap();
216+
let expected = "Hello\nWorld\t\r\\";
217+
assert_eq!(result, expected);
218+
}
219+
220+
#[test]
221+
fn test_unescape_quotes() {
222+
let input = "He said \\\"Hello\\\" and she said \\'Hi\\'";
223+
let result = unescape(input).unwrap();
224+
let expected = "He said \"Hello\" and she said 'Hi'";
225+
assert_eq!(result, expected);
226+
}
227+
228+
#[test]
229+
fn test_unescape_unicode_valid() {
230+
let input = "Unicode: \\u0041\\u0042\\u2764";
231+
let result = unescape(input).unwrap();
232+
let expected = "Unicode: AB❤";
233+
assert_eq!(result, expected);
234+
}
235+
236+
#[test]
237+
fn test_unescape_hex_valid() {
238+
let input = "Hex: \\x41\\x42\\x21";
239+
let result = unescape(input).unwrap();
240+
let expected = "Hex: AB!";
241+
assert_eq!(result, expected);
242+
}
243+
244+
#[test]
245+
fn test_unescape_invalid_unicode_short() {
246+
let input = "Invalid: \\u12G"; // Invalid hex digit
247+
let result = unescape(input);
248+
assert!(result.is_err(), "Should fail on invalid unicode escape");
249+
}
250+
251+
#[test]
252+
fn test_unescape_invalid_unicode_incomplete() {
253+
let input = "Incomplete: \\u123"; // Too few digits
254+
let result = unescape(input);
255+
assert!(result.is_err(), "Should fail on incomplete unicode escape");
256+
}
257+
258+
#[test]
259+
fn test_unescape_invalid_hex_char() {
260+
let input = "Invalid hex: \\xZZ";
261+
let result = unescape(input);
262+
assert!(result.is_err(), "Should fail on invalid hex escape");
263+
}
264+
265+
#[test]
266+
fn test_unescape_invalid_hex_incomplete() {
267+
let input = "Incomplete hex: \\x1";
268+
let result = unescape(input);
269+
assert!(result.is_err(), "Should fail on incomplete hex escape");
270+
}
271+
272+
#[test]
273+
fn test_unescape_unknown_escape() {
274+
let input = "Unknown: \\z";
275+
let result = unescape(input);
276+
assert!(result.is_err(), "Should fail on unknown escape sequence");
277+
}
278+
279+
#[test]
280+
fn test_unescape_incomplete_escape_at_end() {
281+
let input = "Incomplete: \\";
282+
let result = unescape(input);
283+
assert!(result.is_err(), "Should fail on incomplete escape at end");
284+
}
285+
286+
#[test]
287+
fn test_unescape_line_continuation() {
288+
let input = "Line\\ncontinuation";
289+
let result = unescape(input).unwrap();
290+
let expected = "Line\ncontinuation";
291+
assert_eq!(result, expected);
292+
}
293+
294+
#[test]
295+
fn test_unescape_all_special_chars() {
296+
let input = "\\a\\b\\f\\n\\r\\t\\v\\0\\\\\\'\\\"";
297+
let result = unescape(input).unwrap();
298+
let expected = "\x07\x08\x0C\n\r\t\x0B\0\\'\"";
299+
assert_eq!(result, expected);
300+
}
301+
302+
#[test]
303+
fn test_unescape_unicode_line_separators() {
304+
let input = "\\u2028\\u2029"; // Line separator, Paragraph separator
305+
let result = unescape(input).unwrap();
306+
let expected = "\u{2028}\u{2029}";
307+
assert_eq!(result, expected);
308+
}
309+
310+
#[test]
311+
fn test_read_hex_digits_valid() {
312+
let mut chars = "ABCD".chars().peekable();
313+
let result = read_hex_digits(&mut chars, 4, "\\u").unwrap();
314+
assert_eq!(result, 0xABCD);
315+
}
316+
317+
#[test]
318+
fn test_read_hex_digits_invalid_char() {
319+
let mut chars = "12G4".chars().peekable();
320+
let result = read_hex_digits(&mut chars, 4, "\\u");
321+
assert!(result.is_err(), "Should fail on invalid hex character");
322+
}
323+
324+
#[test]
325+
fn test_read_hex_digits_incomplete() {
326+
let mut chars = "12".chars().peekable();
327+
let result = read_hex_digits(&mut chars, 4, "\\u");
328+
assert!(result.is_err(), "Should fail on incomplete hex sequence");
329+
}
330+
}

0 commit comments

Comments
 (0)