const MAX_RENDERED_LEN: usize = 512;
const TRUNCATION_MARKER: &str = "… [truncated]";
pub(crate) fn operator_safe(s: &str) -> String {
let mut out = String::with_capacity(s.len().min(MAX_RENDERED_LEN));
let mut pending_space = false;
for ch in s.chars() {
let keep = match ch {
'\t' | '\n' | '\r' => {
pending_space = true;
continue;
}
c if (c as u32) < 0x20 || c == '\u{7f}' => false,
c if ('\u{80}'..='\u{9f}').contains(&c) => false,
'\u{202a}'..='\u{202e}' | '\u{2066}'..='\u{2069}' => false,
'\u{200b}'..='\u{200f}' | '\u{feff}' => false,
' ' => {
pending_space = true;
continue;
}
_ => true,
};
if !keep {
continue;
}
if pending_space && !out.is_empty() {
out.push(' ');
}
pending_space = false;
out.push(ch);
}
if out.chars().count() > MAX_RENDERED_LEN {
let cut: String = out.chars().take(MAX_RENDERED_LEN).collect();
return format!("{cut}{TRUNCATION_MARKER}");
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_summary_cannot_rewrite_the_line_it_is_printed_on() {
let hostile = "install vim\u{1b}[2K\rremove nothing";
let safe = operator_safe(hostile);
assert!(!safe.contains('\u{1b}'), "ESC survived: {safe:?}");
assert!(!safe.contains('\r'), "CR survived: {safe:?}");
assert_eq!(safe, "install vim[2K remove nothing");
}
#[test]
fn a_summary_cannot_move_the_cursor_off_its_own_line() {
for seq in ["\u{1b}[1A", "\u{1b}[10;10H", "\u{1b}[s\u{1b}[u", "\u{1b}M"] {
let safe = operator_safe(&format!("upgrade{seq}kernel"));
assert!(
!safe.contains('\u{1b}'),
"ESC survived from {seq:?}: {safe:?}"
);
}
}
#[test]
fn c1_control_characters_are_dropped_too() {
let safe = operator_safe("upgrade\u{9b}2K\rkernel");
assert!(!safe.contains('\u{9b}'), "C1 CSI survived: {safe:?}");
}
#[test]
fn a_summary_cannot_forge_extra_lines() {
let safe = operator_safe("install vim\n\n 3 RemoveSwap LOW auto");
assert!(!safe.contains('\n'), "newline survived: {safe:?}");
assert_eq!(safe, "install vim 3 RemoveSwap LOW auto");
}
#[test]
fn bidi_overrides_and_zero_width_characters_are_dropped() {
for ch in [
'\u{202a}', '\u{202b}', '\u{202c}', '\u{202d}', '\u{202e}', '\u{2066}', '\u{2067}',
'\u{2068}', '\u{2069}', '\u{200b}', '\u{200e}', '\u{feff}',
] {
let safe = operator_safe(&format!("remove{ch}nothing"));
assert!(!safe.contains(ch), "U+{:04X} survived: {safe:?}", ch as u32);
}
}
#[test]
fn an_overlong_summary_is_capped_and_says_so() {
let safe = operator_safe(&"A".repeat(10_000));
assert!(
safe.chars().count() <= MAX_RENDERED_LEN + TRUNCATION_MARKER.chars().count(),
"not capped: {} chars",
safe.chars().count()
);
assert!(safe.ends_with(TRUNCATION_MARKER), "truncation not marked");
}
#[test]
fn truncation_respects_char_boundaries() {
let safe = operator_safe(&"🔒".repeat(10_000));
assert!(safe.ends_with(TRUNCATION_MARKER));
assert!(safe.chars().count() <= MAX_RENDERED_LEN + TRUNCATION_MARKER.chars().count());
}
#[test]
fn ordinary_summaries_pass_through_intact() {
for s in [
"install vim and git",
"restart nginx.service",
"add 2 GiB of swap at /swapfile",
"rotate /var/log/nginx/*.log daily, keep 14",
"café — naïve — 日本語 — emoji 🔒 all survive",
] {
assert_eq!(operator_safe(s), s, "mangled an ordinary summary");
}
}
#[test]
fn whitespace_collapses_and_trims() {
assert_eq!(operator_safe(" install vim \t\n "), "install vim");
assert_eq!(operator_safe(""), "");
assert_eq!(operator_safe("\u{1b}\u{1b}\u{1b}"), "");
}
}