expandtabs_rs/
lib.rs

1use std::borrow::Cow;
2use std::fmt::Write;
3
4pub trait StringExt {
5    fn expand_tabs_default(&self) -> Cow<str> {
6        self.expand_tabs(8)
7    }
8
9    fn expand_tabs(&self, tab_size: u16) -> Cow<str>;
10}
11
12impl<T> StringExt for T
13where
14    T: AsRef<str>,
15{
16    fn expand_tabs(&self, tab_size: u16) -> Cow<str> {
17        let s = self.as_ref();
18        let tab = '\t';
19        if s.contains(tab) {
20            let mut res = String::new();
21            let mut last_pos = 0;
22
23            while let Some(pos) = &s[last_pos..].find(tab) {
24                res.push_str(&s[last_pos..*pos + last_pos]);
25
26                let spaces_to_add = if tab_size != 0 {
27                    tab_size - (*pos as u16 % tab_size)
28                } else {
29                    0
30                };
31
32                if spaces_to_add != 0 {
33                    let _ = write!(res, "{:width$}", "", width = spaces_to_add as usize);
34                }
35
36                last_pos += *pos + 1;
37            }
38
39            res.push_str(&s[last_pos..]);
40
41            Cow::from(res)
42        } else {
43            Cow::from(s)
44        }
45    }
46}
47
48#[cfg(test)]
49mod tests {
50    use super::StringExt;
51
52    #[test]
53    fn test_default_tab_size_works() {
54        assert_eq!("H       e", "H\te".expand_tabs_default());
55    }
56
57    #[test]
58    fn test_tab_size_of_two_works() {
59        assert_eq!("H e", "H\te".expand_tabs(2));
60    }
61
62    #[test]
63    fn test_tab_size_of_four_works() {
64        assert_eq!("H   e", "H\te".expand_tabs(4));
65    }
66
67    #[test]
68    fn test_tab_size_of_one_works() {
69        assert_eq!("H e", "H\te".expand_tabs(1));
70    }
71
72    #[test]
73    fn test_tab_size_of_zero_works() {
74        assert_eq!("He", "H\te".expand_tabs(0));
75    }
76
77    #[test]
78    fn test_tab_size_of_three_works() {
79        assert_eq!("H  e", "H\te".expand_tabs(3));
80    }
81
82    #[test]
83    fn test_tab_size_of_nine_works() {
84        assert_eq!("H        e", "H\te".expand_tabs(9));
85    }
86
87    #[test]
88    fn test_tab_at_position_larger_than_tab_size_works() {
89        assert_eq!("Hello World", "Hello\tWorld".expand_tabs(1));
90        assert_eq!("HelloWorld", "Hello\tWorld".expand_tabs(0));
91        assert_eq!("Hello   World", "Hello\tWorld".expand_tabs(4));
92    }
93
94    #[test]
95    fn test_expand_multiple_tabs_works() {
96        assert_eq!("H ello World", "H\tello\tWorld".expand_tabs(1));
97    }
98}