1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
pub trait StrExt {
    fn char_at(&self, idx: usize) -> Option<char>;
    fn previous(&self, idx: usize) -> Option<char>;
}

impl StrExt for str {
    fn char_at(&self, at: usize) -> Option<char> {
        self.char_indices()
            .skip_while(|&(idx, _c)| idx < at)
            .filter_map(|(idx, c)| if idx == at { Some(c) } else { None })
            .next()
    }

    fn previous(&self, current: usize) -> Option<char> {
        self.char_indices()
            .skip_while(|&(idx, c)| idx + c.len_utf8() < current)
            .filter_map(|(idx, c)| {
                if idx + c.len_utf8() == current {
                    Some(c)
                } else {
                    None
                }
            })
            .next()
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn various_len() {
        let data = "абвГДеёlжз";
        assert_eq!(Some('а'), data.char_at(0));
        assert_eq!(Some('б'), data.char_at(2));
        assert_eq!(Some('в'), data.char_at(4));
        assert_eq!(Some('Г'), data.char_at(6));
        assert_eq!(Some('Д'), data.char_at(8));
        assert_eq!(Some('е'), data.char_at(10));
        assert_eq!(Some('ё'), data.char_at(12));
        assert_eq!(Some('l'), data.char_at(14));
        assert_eq!(Some('ж'), data.char_at(15));
        assert_eq!(Some('з'), data.char_at(17));
        assert_eq!(None, data.previous(0));
        assert_eq!(Some('а'), data.previous(2));
        assert_eq!(Some('б'), data.previous(4));
        assert_eq!(Some('в'), data.previous(6));
        assert_eq!(Some('Г'), data.previous(8));
        assert_eq!(Some('Д'), data.previous(10));
        assert_eq!(Some('е'), data.previous(12));
        assert_eq!(Some('ё'), data.previous(14));
        assert_eq!(Some('l'), data.previous(15));
        assert_eq!(Some('ж'), data.previous(17));
    }

    #[test]
    fn boundary() {
        let data = "ф";
        assert_eq!(None, data.char_at(1));
    }
}