#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Context {
Text, Link, Verbatim, }
pub fn escape(plain: &str, context: Context) -> String {
let mut escaped = String::new();
for character in plain.chars() {
if is_escapable(character, context) {
escaped.push('\\');
}
escaped.push(character);
}
escaped
}
pub fn unescape(source: &str, context: Context) -> Option<String> {
let mut plain = String::new();
let mut characters = source.chars();
while let Some(character) = characters.next() {
plain.push(if character == '\\' && has_escapes(context) {
characters
.next()
.filter(|&next| is_escapable(next, context))?
} else {
character
});
}
Some(plain)
}
pub fn is_escapable(character: char, context: Context) -> bool {
match context {
Context::Text => matches!(character, '\\' | '[' | ']' | '`'),
Context::Link => matches!(character, '\\' | '[' | ']'),
Context::Verbatim => false,
}
}
pub fn invalid_escape_message(context: Context) -> &'static str {
match context {
Context::Text => {
"A backslash must be followed by `[`, `]`, `` ` ``, or `\\`, so write `\\\\` for a \
backslash itself."
}
Context::Link => {
"In a link, a backslash must be followed by `[`, `]`, or `\\`, so write `\\\\` for a \
backslash itself."
}
Context::Verbatim => "Here, a backslash is a plain character.",
}
}
fn has_escapes(context: Context) -> bool {
context != Context::Verbatim
}
#[cfg(test)]
mod tests {
use super::{Context, escape, unescape};
#[test]
fn escaping() {
assert_eq!(escape(r"a\[b]\c`", Context::Text), r"a\\\[b\]\\c\`");
assert_eq!(escape(r"a\[b]\c`", Context::Link), r"a\\\[b\]\\c`");
assert_eq!(
unescape(r"a\\\[b\]\\c\\\``", Context::Text),
Some(r"a\[b]\c\``".to_owned()),
);
assert_eq!(
unescape(r"a\\\[b\]\\c\\`", Context::Link),
Some(r"a\[b]\c\`".to_owned()),
);
assert_eq!(unescape(r"\`", Context::Link), None);
assert_eq!(unescape(r"\a", Context::Text), None);
assert_eq!(unescape(r"\#", Context::Text), None);
assert_eq!(unescape(r"a\\\", Context::Text), None);
assert_eq!(escape(r"#\#", Context::Text), r"#\\#");
assert_eq!(escape(r"a\[b]\c`", Context::Verbatim), r"a\[b]\c`");
assert_eq!(
unescape(r"a\[b]\c`\", Context::Verbatim),
Some(r"a\[b]\c`\".to_owned()),
);
}
}