mxmlextrema_as3parser/util/
character_reader.rs

1use std::str::CharIndices;
2
3/// `CharacterReader` may be used for iterating characters from left-to-right
4/// in a string with miscellaneous operation methods.
5#[derive(Clone)]
6pub struct CharacterReader<'a> {
7    length: usize,
8    start_offset: usize,
9    char_indices: CharIndices<'a>,
10}
11
12impl<'a> CharacterReader<'a> {
13    /// Constructs a `CharacterReader` from a string starting at the given offset.
14    pub fn from_offset(value: &'a str, offset: usize) -> Self {
15        CharacterReader { length: value.len(), start_offset: offset, char_indices: value[offset..].char_indices() }
16    }
17
18    /// Indicates if there are remaining code points to read.
19    pub fn has_remaining(&self) -> bool {
20        self.clone().char_indices.next().is_some()
21    }
22
23    /// Indicates if the reader has reached the end of the string.
24    pub fn reached_end(&self) -> bool {
25        self.clone().char_indices.next().is_none()
26    }
27
28    /// Skips a code point in the reader. This is equivalent to
29    /// calling `next()`.
30    pub fn skip_in_place(&mut self) {
31        self.next();
32    }
33
34    /// Skips the given number of code points in the reader.
35    pub fn skip_count_in_place(&mut self, count: usize) {
36        for _ in 0..count {
37            if self.next().is_none() {
38                break;
39            }
40        }
41    }
42
43    /// Returns the current byte offset in the string.
44    pub fn index(&self) -> usize {
45        self.clone().char_indices.next().map_or(self.length, |(i, _)| self.start_offset + i)
46    }
47
48    /// Returns the next code point. If there are no code points
49    /// available, returns U+00.
50    pub fn next_or_zero(&mut self) -> char {
51        self.char_indices.next().map_or('\x00', |(_, cp)| cp)
52    }
53
54    /// Peeks the next code point.
55    pub fn peek(&self) -> Option<char> {
56        self.clone().char_indices.next().map(|(_, cp)| cp)
57    }
58
59    /// Peeks the next code point. If there are no code points
60    /// available, returns U+00.
61    pub fn peek_or_zero(&self) -> char {
62        self.clone().next_or_zero()
63    }
64
65    /// Peeks the next code point at the given zero based code point index.
66    pub fn peek_at(&self, index: usize) -> Option<char> {
67        let mut indices = self.clone().char_indices;
68        for _ in 0..index {
69            if indices.next().is_none() {
70                break;
71            }
72        }
73        indices.next().map(|(_, cp)| cp)
74    }
75
76    /// Peeks the next code point at the given zero based code point index.
77    /// If there are no code points available, returns U+00.
78    pub fn peek_at_or_zero(&self, index: usize) -> char {
79        self.peek_at(index).unwrap_or('\x00')
80    }
81
82    /// Peeks a number of code points until the string's end.
83    pub fn peek_seq(&self, num_code_points: u64) -> String {
84        let mut r = String::new();
85        let mut next_indices = self.char_indices.clone();
86        for _ in 0..num_code_points {
87            match next_indices.next() {
88                None => {
89                    break;
90                },
91                Some(cp) => {
92                    r.push(cp.1);
93                }
94            }
95        }
96        r
97    }
98}
99
100impl<'a> From<&'a str> for CharacterReader<'a> {
101    /// Constructs a `CharacterReader` from a string.
102    fn from(value: &'a str) -> Self {
103        CharacterReader { length: value.len(), start_offset: 0, char_indices: value.char_indices() }
104    }
105}
106
107impl<'a> From<&'a String> for CharacterReader<'a> {
108    /// Constructs a `CharacterReader` from a string.
109    fn from(value: &'a String) -> Self {
110        CharacterReader { length: value.len(), start_offset: 0, char_indices: value.char_indices() }
111    }
112}
113
114impl<'a> Iterator for CharacterReader<'a> {
115    type Item = char;
116
117    fn next(&mut self) -> Option<Self::Item> {
118        self.char_indices.next().map(|(_, cp)| cp)
119    }
120}
121
122#[cfg(test)]
123mod test {
124    use super::CharacterReader;
125    #[test]
126    fn test() {
127        let mut reader = CharacterReader::from("foo");
128        assert!(reader.has_remaining());
129        assert_eq!(reader.peek_seq(5), "foo");
130        assert_eq!(reader.peek_seq(1), "f");
131        assert_eq!(reader.peek_or_zero(), 'f');
132        for _ in 0..3 {
133            reader.next();
134        }
135        assert_eq!(reader.peek_or_zero(), '\x00');
136        assert!(reader.reached_end());
137    }
138}