Skip to main content

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}