use std::borrow::Cow;
pub fn scrub_ctrl(s: &str) -> Cow<'_, str> {
let needs_scrubbing = s.bytes().any(is_offending);
if !needs_scrubbing {
return Cow::Borrowed(s);
}
let mut out = String::with_capacity(s.len());
for ch in s.chars() {
let code = ch as u32;
if code < 0x80 && is_offending(code as u8) {
out.push('?');
} else {
out.push(ch);
}
}
Cow::Owned(out)
}
#[inline]
fn is_offending(b: u8) -> bool {
(b < 0x20 && b != b'\t') || b == 0x7f
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn clean_ascii_is_borrowed() {
let s = "nginx-prod-01";
let out = scrub_ctrl(s);
assert!(matches!(out, Cow::Borrowed(_)));
assert_eq!(out, "nginx-prod-01");
}
#[test]
fn empty_string_is_borrowed() {
let out = scrub_ctrl("");
assert!(matches!(out, Cow::Borrowed(_)));
}
#[test]
fn tab_is_preserved() {
let s = "col1\tcol2";
let out = scrub_ctrl(s);
assert!(matches!(out, Cow::Borrowed(_)));
assert_eq!(out, "col1\tcol2");
}
#[test]
fn osc_sequence_is_replaced() {
let s = "\x1b]0;evil\x07";
let out = scrub_ctrl(s);
assert!(matches!(out, Cow::Owned(_)));
assert_eq!(out, "?]0;evil?");
}
#[test]
fn csi_color_sequence_is_replaced() {
let s = "\x1b[31mRED\x1b[0m";
let out = scrub_ctrl(s);
assert!(matches!(out, Cow::Owned(_)));
assert_eq!(out, "?[31mRED?[0m");
}
#[test]
fn newline_and_carriage_return_are_replaced() {
let s = "line1\nline2\rline3";
let out = scrub_ctrl(s);
assert!(matches!(out, Cow::Owned(_)));
assert_eq!(out, "line1?line2?line3");
}
#[test]
fn del_byte_is_replaced() {
let s = "abc\x7fdef";
let out = scrub_ctrl(s);
assert!(matches!(out, Cow::Owned(_)));
assert_eq!(out, "abc?def");
}
#[test]
fn null_byte_is_replaced() {
let s = "abc\x00def";
let out = scrub_ctrl(s);
assert!(matches!(out, Cow::Owned(_)));
assert_eq!(out, "abc?def");
}
#[test]
fn multibyte_utf8_is_preserved() {
let s = "café 中文 日本語";
let out = scrub_ctrl(s);
assert!(matches!(out, Cow::Borrowed(_)));
assert_eq!(&*out, s);
let bytes: Vec<u8> = out.as_bytes().to_vec();
assert_eq!(String::from_utf8(bytes).unwrap(), s);
}
#[test]
fn multibyte_with_control_strips_only_control() {
let s = "café\x1bbar";
let out = scrub_ctrl(s);
assert!(matches!(out, Cow::Owned(_)));
assert_eq!(&*out, "café?bar");
}
#[test]
fn single_escape_is_replaced() {
let s = "\x1b";
let out = scrub_ctrl(s);
assert_eq!(&*out, "?");
}
}