1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//! Backslash escapes inside double-quoted ABC values (field values such as
//! `name="…"`, `Q:"…"` tempo text, and croma carrier `key="…"` values).
/// Undo the escaping croma's ABC writer applies to quoted text: `\\` is a
/// backslash and `\"` a double quote. Any other backslash sequence, and a
/// trailing backslash, is kept as written, so ABC 2.1 §8.2 text-string
/// mnemonics such as `\'e` reach the output intact rather than losing their
/// backslash.
pub(crate) fn unescape_quoted(text: &str) -> String {
let mut out = String::with_capacity(text.len());
let mut chars = text.chars();
while let Some(ch) = chars.next() {
if ch != '\\' {
out.push(ch);
continue;
}
match chars.next() {
Some(escaped @ ('"' | '\\')) => out.push(escaped),
Some(other) => {
out.push('\\');
out.push(other);
}
None => out.push('\\'),
}
}
out
}
/// Whether the byte at `offset` in `text` is escaped: preceded by an odd run of
/// backslashes.
pub(crate) fn is_escaped(text: &str, offset: usize) -> bool {
let mut slash_count = 0;
for byte in text[..offset].bytes().rev() {
if byte == b'\\' {
slash_count += 1;
} else {
break;
}
}
slash_count % 2 == 1
}
#[cfg(test)]
#[path = "escape_tests.rs"]
mod tests;