rusty_bubbles/internal/runeutil.rs
1//! Cleanroom Rust port of upstream Go source file: `internal/runeutil/runeutil.go`
2//! Upstream Target Tag / Version: `v2.1.0`
3
4/// Sanitizer is a helper for bubble widgets that want to process
5/// Runes from input key messages.
6pub trait Sanitizer {
7 /// Sanitize removes control characters from runes in a KeyRunes
8 /// message, and optionally replaces newline/carriage return/tabs by a
9 /// specified character.
10 ///
11 /// The rune array is modified in-place if possible. In that case, the
12 /// returned slice is the original slice shortened after the control
13 /// characters have been removed/translated.
14 fn sanitize(&self, runes: &[char]) -> Vec<char>;
15}
16
17/// NewSanitizer constructs a rune sanitizer.
18pub fn new_sanitizer(opts: Vec<Option>) -> Sanitizer_ {
19 let mut s = Sanitizer_ {
20 replace_new_line: "\n".chars().collect(),
21 replace_tab: " ".chars().collect(),
22 };
23 for o in opts {
24 s = o(s);
25 }
26 s
27}
28
29/// Option is the type of option that can be passed to Sanitize().
30pub type Option = Box<dyn FnOnce(Sanitizer_) -> Sanitizer_>;
31
32/// ReplaceTabs replaces tabs by the specified string.
33pub fn replace_tabs(tab_repl: &str) -> Option {
34 let tab_repl = tab_repl.chars().collect();
35 Box::new(move |s: Sanitizer_| Sanitizer_ {
36 replace_tab: tab_repl,
37 ..s
38 })
39}
40
41/// ReplaceNewlines replaces newline characters by the specified string.
42pub fn replace_newlines(nl_repl: &str) -> Option {
43 let nl_repl = nl_repl.chars().collect();
44 Box::new(move |s: Sanitizer_| Sanitizer_ {
45 replace_new_line: nl_repl,
46 ..s
47 })
48}
49
50#[derive(Clone)]
51pub struct Sanitizer_ {
52 replace_new_line: Vec<char>,
53 replace_tab: Vec<char>,
54}
55
56impl Sanitizer for Sanitizer_ {
57 fn sanitize(&self, runes: &[char]) -> Vec<char> {
58 // dstrunes are where we are storing the result.
59 let mut dstrunes: Vec<char> = Vec::with_capacity(runes.len());
60
61 for r in runes {
62 match r {
63 // invalid utf8 replacement char: skip
64 &'\u{FFFD}' => {}
65
66 &'\r' | &'\n' => {
67 dstrunes.extend_from_slice(&self.replace_new_line);
68 }
69
70 &'\t' => {
71 dstrunes.extend_from_slice(&self.replace_tab);
72 }
73
74 c if c.is_control() => {
75 // Other control characters: skip.
76 }
77
78 _ => {
79 // Keep the character.
80 dstrunes.push(*r);
81 }
82 }
83 }
84 dstrunes
85 }
86}