Skip to content

Commit 96cc58d

Browse files
authored
Merge pull request #22 from spyoungtech/fix-escaping-in-serialization
fix escaping in serialization
2 parents 1eba2a0 + 7718c0b commit 96cc58d

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)