use std::borrow::Cow;
pub fn parse_string(input: &str) -> Cow<str> {
let input = slice_middle(input);
if !input.contains('\\') {
return Cow::Borrowed(input);
}
let mut output = String::new();
let mut wants_escape = false;
for ch in input.chars() {
if wants_escape {
let replacement = match ch {
'\\' => '\\',
'\"' => '\"',
'\'' => '\'',
'r' => '\r',
'n' => '\n',
't' => '\t',
_ => panic!("Invalid escape sequence: '\\{}'", ch),
};
output.push(replacement);
wants_escape = false;
} else if ch == '\\' {
wants_escape = true;
} else {
output.push(ch);
}
}
Cow::Owned(output)
}
fn slice_middle(input: &str) -> &str {
let len = input.len();
let last = len - 1;
&input[1..last]
}
#[test]
fn test_parse_string() {
macro_rules! test {
($input:expr, $expected:expr, $variant:tt $(,)?) => {{
let actual = parse_string($input);
assert_eq!(
&actual, $expected,
"Actual string (left) doesn't match expected (right)"
);
assert!(
matches!(actual, Cow::$variant(_)),
"Outputted string of the incorrect variant",
);
}};
}
test!(r#""""#, "", Borrowed);
test!(r#""!""#, "!", Borrowed);
test!(r#""apple banana""#, "apple banana", Borrowed);
test!(r#""abc \\""#, "abc \\", Owned);
test!(r#""\n def""#, "\n def", Owned);
test!(
r#""abc \t (\\\t) \r (\\\r) def""#,
"abc \t (\\\t) \r (\\\r) def",
Owned,
);
}
#[test]
fn test_slice_middle() {
macro_rules! test {
($input:expr, $expected:expr $(,)?) => {{
let actual = slice_middle($input);
assert_eq!(
actual, $expected,
"Actual (left) doesn't match expected (right)",
);
}};
}
test!(r#""""#, "");
test!(r#""!""#, "!");
test!(r#""abc""#, "abc");
test!(r#""apple banana cherry""#, "apple banana cherry");
}