#[must_use]
pub fn unescape(escaped: &str) -> Unescape {
let cursor = escaped.starts_with('\\').into();
Unescape { escaped, cursor }
}
#[derive(Debug)]
pub struct Unescape<'a> {
escaped: &'a str,
cursor: usize,
}
impl<'a> Iterator for Unescape<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
if self.cursor >= self.escaped.len() {
return None;
}
let start = self.cursor + 1;
let end = if start >= self.escaped.len() {
self.escaped.len()
} else {
self.escaped[start..]
.find('\\')
.map_or(self.escaped.len(), |i| start + i)
};
let result = Some(&self.escaped[self.cursor..end]);
self.cursor = end + 1;
result
}
}
#[cfg(test)]
mod test {
macro_rules! test_unescape {
($escaped:expr => $($result:expr),* $(,)?) => {
assert_eq!(crate::generator::helper::unescape($escaped).collect::<Vec<_>>(), vec![$($result),+])
};
}
#[test]
fn test_escaped_string_at_begin() {
test_unescape!("\\<b" => "<b");
test_unescape!("\\>b" => ">b");
test_unescape!("\\\\b" => "\\b");
}
#[test]
fn test_escaped_string_at_middle() {
test_unescape!("a\\<b" => "a", "<b");
test_unescape!("a\\>b" => "a", ">b");
test_unescape!("a\\\\b" => "a", "\\b");
}
#[test]
fn test_escaped_string_at_end() {
test_unescape!("a\\<" => "a", "<");
test_unescape!("a\\>" => "a", ">");
test_unescape!("a\\\\" => "a", "\\");
}
#[test]
fn test_escaped_string_multi() {
test_unescape!("1\\<2\\<3 \\\\ 3\\>2\\>1" => "1", "<2", "<3 ", "\\ 3", ">2", ">1");
}
}