1use ratatui::buffer::Buffer;
11
12const REPLACEMENT: char = '\u{fffd}';
15
16#[inline]
19fn is_forbidden(c: char) -> bool {
20 let n = c as u32;
21 (n < 0x20 && c != '\t') || n == 0x7f || (0x80..=0x9f).contains(&n)
22}
23
24pub fn sanitized(s: &str) -> Option<String> {
27 if !s.chars().any(is_forbidden) {
28 return None;
29 }
30 Some(
31 s.chars()
32 .map(|c| if is_forbidden(c) { REPLACEMENT } else { c })
33 .collect(),
34 )
35}
36
37pub fn sanitize_buffer(buf: &mut Buffer) {
40 for cell in buf.content.iter_mut() {
41 if let Some(clean) = sanitized(cell.symbol()) {
42 cell.set_symbol(&clean);
43 }
44 }
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50 use ratatui::layout::Rect;
51
52 #[test]
53 fn leaves_ordinary_text_alone() {
54 assert_eq!(sanitized("hello"), None);
55 assert_eq!(sanitized(""), None);
56 assert_eq!(sanitized("a\tb"), None);
58 assert_eq!(sanitized("héllo · 日本語 · 🦀"), None);
60 }
61
62 #[test]
63 fn replaces_c0_controls() {
64 assert_eq!(sanitized("\x1b[2J").unwrap(), "\u{fffd}[2J");
65 assert_eq!(sanitized("a\x07b").unwrap(), "a\u{fffd}b");
66 assert_eq!(sanitized("a\rb").unwrap(), "a\u{fffd}b");
67 assert_eq!(sanitized("a\nb").unwrap(), "a\u{fffd}b");
68 assert_eq!(sanitized("a\x00b").unwrap(), "a\u{fffd}b");
69 }
70
71 #[test]
72 fn replaces_del_and_c1() {
73 assert_eq!(sanitized("a\x7fb").unwrap(), "a\u{fffd}b");
74 assert_eq!(sanitized("\u{009b}31m").unwrap(), "\u{fffd}31m");
76 assert_eq!(sanitized("\u{0080}").unwrap(), "\u{fffd}");
77 assert_eq!(sanitized("\u{009f}").unwrap(), "\u{fffd}");
78 }
79
80 #[test]
81 fn keeps_the_character_after_the_c1_range() {
82 assert_eq!(sanitized("\u{00a0}"), None);
84 }
85
86 #[test]
87 fn sweeps_a_whole_buffer() {
88 let mut buf = Buffer::empty(Rect::new(0, 0, 4, 1));
89 buf[(0, 0)].set_symbol("a");
90 buf[(1, 0)].set_symbol("\x1b");
91 buf[(2, 0)].set_symbol("b\x1b]52;c;x\x07");
95 buf[(3, 0)].set_symbol("c");
96
97 sanitize_buffer(&mut buf);
98
99 assert_eq!(buf[(0, 0)].symbol(), "a");
100 assert_eq!(buf[(1, 0)].symbol(), "\u{fffd}");
101 assert_eq!(buf[(2, 0)].symbol(), "b\u{fffd}]52;c;x\u{fffd}");
102 assert_eq!(buf[(3, 0)].symbol(), "c");
103 }
104}