pub fn strip_ansi(input: &str) -> String {
let mut out = String::with_capacity(input.len());
let mut chars = input.chars().peekable();
while let Some(ch) = chars.next() {
if ch == '\x1b' {
match chars.peek() {
Some(&'[') => {
chars.next(); while let Some(&next) = chars.peek() {
chars.next();
if (0x40..=0x7E).contains(&(next as u32)) {
break;
}
}
continue;
}
Some(&']') => {
chars.next(); while let Some(&next) = chars.peek() {
chars.next();
if next == '\x07' {
break;
}
if next == '\x1b' {
if let Some(&'\\') = chars.peek() {
chars.next();
break;
}
}
}
continue;
}
_ => {}
}
}
out.push(ch);
}
out
}
#[macro_export]
macro_rules! assert_plain_snapshot {
($actual:expr, $expected:expr) => {
let plain = $crate::testing::strip_ansi(&$actual);
assert_eq!(plain.trim(), $expected.trim());
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_strip_ansi_colors() {
let colored = "\x1b[31mError\x1b[0m occurred";
assert_eq!(strip_ansi(colored), "Error occurred");
}
#[test]
fn test_strip_ansi_osc8_hyperlinks() {
let linked = "\x1b]8;;https://example.com\x1b\\\x1b[36mexample\x1b[0m\x1b]8;;\x1b\\";
assert_eq!(strip_ansi(linked), "example");
}
#[test]
fn test_strip_ansi_osc8_bel_terminator() {
let linked = "\x1b]8;;https://example.com\x07bel-link\x1b]8;;\x07";
assert_eq!(strip_ansi(linked), "bel-link");
}
}