pub fn is_unsafe(c: char) -> bool {
c.is_control()
|| matches!(
c,
'\u{202a}'..='\u{202e}' | '\u{2066}'..='\u{2069}' | '\u{200e}' | '\u{200f}' | '\u{061c}'
)
}
pub fn clean(text: &str) -> String {
let kept: String = text
.chars()
.map(|c| if c.is_whitespace() { ' ' } else { c })
.filter(|&c| !is_unsafe(c))
.collect();
kept.split_whitespace().collect::<Vec<_>>().join(" ")
}
pub fn clean_pre(text: &str) -> String {
let mut out = String::with_capacity(text.len());
let mut column = 0;
for c in text.chars() {
match c {
'\n' => {
out.push('\n');
column = 0;
}
'\t' => {
let next = (column / 4 + 1) * 4;
out.extend(std::iter::repeat_n(' ', next - column));
column = next;
}
'\r' => {}
c if is_unsafe(c) => {}
c => {
out.push(c);
column += 1;
}
}
}
out
}
pub fn clean_inline(text: &str) -> String {
let body = clean(text);
if body.is_empty() {
return if text.chars().any(char::is_whitespace) {
" ".into()
} else {
String::new()
};
}
let lead = text.starts_with(char::is_whitespace);
let trail = text.ends_with(char::is_whitespace);
format!(
"{}{body}{}",
if lead { " " } else { "" },
if trail { " " } else { "" }
)
}
pub fn markup(text: &str) -> String {
rich::markup::escape(&clean(text))
}
pub fn truncate(text: &str, max: usize) -> String {
if text.chars().count() <= max {
return text.to_string();
}
let mut out: String = text.chars().take(max.saturating_sub(1)).collect();
out.push('…');
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn no_control_character_survives() {
assert_eq!(clean("a\x1b[31mb\u{9b}2Jc\u{202e}d\u{7f}"), "a[31mb2Jcd");
assert_eq!(clean(" two\n\t lines "), "two lines");
assert_eq!(clean("x\u{2066}y\u{2069}\u{200f}z"), "xyz");
for text in [
clean("\x07\x1b]0;t\x07"),
clean_pre("\x1b[2J\n\x1b"),
clean_inline(" \x1b "),
] {
assert!(!text.chars().any(|c| c != '\n' && is_unsafe(c)), "{text:?}");
}
}
#[test]
fn preformatted_text_keeps_its_lines() {
assert_eq!(clean_pre("a\tb\r\n c\x1b"), "a b\n c");
}
#[test]
fn inline_runs_keep_one_space_at_their_ends() {
assert_eq!(clean_inline(" of the\n "), " of the ");
assert_eq!(clean_inline("\n "), " ");
assert_eq!(clean_inline(""), "");
}
#[test]
fn page_markup_is_escaped() {
assert_eq!(markup("[bold]x[/]"), "\\[bold]x\\[/]");
assert_eq!(truncate("abcdef", 4), "abc…");
assert_eq!(truncate("abc", 4), "abc");
}
}