1#[derive(Copy, Clone)]
15enum CommaSeparatedIteratorState {
16 Default,
18 Quoted(Quote),
20 QuotedEscape(Quote),
22}
23
24#[derive(Copy, Clone)]
25enum Quote {
26 Single,
27 Double,
28}
29
30pub struct CommaSeparatedIterator<'a> {
31 remaining: &'a str,
32}
33
34impl<'a> CommaSeparatedIterator<'a> {
35 pub fn new(text: &'a str) -> Self {
37 Self { remaining: text }
38 }
39}
40
41impl<'a> Iterator for CommaSeparatedIterator<'a> {
42 type Item = &'a str;
43
44 fn next(&mut self) -> Option<Self::Item> {
45 if self.remaining.is_empty() {
46 return None;
47 }
48
49 let mut state = CommaSeparatedIteratorState::Default;
50 let char_indices = self.remaining.char_indices();
51
52 for (i, c) in char_indices {
53 state = match (state, c) {
54 (CommaSeparatedIteratorState::Default, '"') => {
55 CommaSeparatedIteratorState::Quoted(Quote::Double)
56 }
57 (CommaSeparatedIteratorState::Default, '\'') => {
58 CommaSeparatedIteratorState::Quoted(Quote::Single)
59 }
60 (CommaSeparatedIteratorState::Quoted(Quote::Double), '"')
61 | (CommaSeparatedIteratorState::Quoted(Quote::Single), '\'') => {
62 CommaSeparatedIteratorState::Default
63 }
64 (CommaSeparatedIteratorState::Quoted(quote), '\\') => {
65 CommaSeparatedIteratorState::QuotedEscape(quote)
66 }
67 (CommaSeparatedIteratorState::Quoted(quote), _) => {
68 CommaSeparatedIteratorState::Quoted(quote)
69 }
70 (CommaSeparatedIteratorState::QuotedEscape(quote), _) => {
71 CommaSeparatedIteratorState::Quoted(quote)
72 }
73 (CommaSeparatedIteratorState::Default, ',') => {
74 let result = &self.remaining[0..i];
75 self.remaining = &self.remaining[i + 1..];
76 return Some(result);
77 }
78 (CommaSeparatedIteratorState::Default, _) => CommaSeparatedIteratorState::Default,
79 };
80 }
81 let result = self.remaining;
82 self.remaining = "";
83 Some(result)
84 }
85}
86
87impl<'a> DoubleEndedIterator for CommaSeparatedIterator<'a> {
88 fn next_back(&mut self) -> Option<Self::Item> {
89 if self.remaining.is_empty() {
90 return None;
91 }
92
93 let mut state = CommaSeparatedIteratorState::Default;
94 let mut char_indices = self.remaining.char_indices().rev().peekable();
95
96 while let Some((i, c)) = char_indices.next() {
97 state = match (state, c) {
98 (CommaSeparatedIteratorState::Default, '"') => {
99 CommaSeparatedIteratorState::Quoted(Quote::Double)
100 }
101 (CommaSeparatedIteratorState::Default, '\'') => {
102 CommaSeparatedIteratorState::Quoted(Quote::Single)
103 }
104 (CommaSeparatedIteratorState::Quoted(quote @ Quote::Double), '"')
105 | (CommaSeparatedIteratorState::Quoted(quote @ Quote::Single), '\'') => {
106 if char_indices.peek().map(|(_, c)| *c) == Some('\\') {
107 CommaSeparatedIteratorState::Quoted(quote)
108 } else {
109 CommaSeparatedIteratorState::Default
110 }
111 }
112 (CommaSeparatedIteratorState::Quoted(quote), _) => {
113 CommaSeparatedIteratorState::Quoted(quote)
114 }
115 (CommaSeparatedIteratorState::QuotedEscape(quote), _) => {
116 CommaSeparatedIteratorState::Quoted(quote)
117 }
118 (CommaSeparatedIteratorState::Default, ',') => {
119 let result = &self.remaining[i + 1..];
120 self.remaining = &self.remaining[0..i];
121 return Some(result);
122 }
123 (CommaSeparatedIteratorState::Default, _) => CommaSeparatedIteratorState::Default,
124 };
125 }
126
127 let result = self.remaining;
128 self.remaining = "";
129 Some(result)
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use crate::CommaSeparatedIterator;
136
137 #[test]
138 fn test_comma_separated_iterator() {
139 assert_eq!(
140 vec!["abc", "def", " ghi", "\tjkl", "mno", "\tpqr"],
141 CommaSeparatedIterator::new("abc,def, ghi,\tjkl,mno,\tpqr").collect::<Vec<&str>>()
142 );
143 assert_eq!(
144 vec!["\tpqr", "mno", "\tjkl", " ghi", "def", "abc"],
145 CommaSeparatedIterator::new("abc,def, ghi,\tjkl,mno,\tpqr")
146 .rev()
147 .collect::<Vec<&str>>()
148 );
149
150 assert_eq!(
151 vec![
152 r#""abc,def""#,
153 " \"ghi\"",
154 "\"jkl\" ",
155 " \"mno\"",
156 "pqr",
157 " \"abc, def\"",
158 " foo",
159 " \" foo\"",
160 " ',foo'",
161 " \"fo'o\"",
162 ],
163 CommaSeparatedIterator::new(
164 r#""abc,def", "ghi","jkl" , "mno",pqr, "abc, def", foo, " foo", ',foo', "fo'o""#
165 )
166 .collect::<Vec<&str>>()
167 );
168 assert_eq!(
169 vec![
170 " \"fo'o\"",
171 " ',foo'",
172 " \" foo\"",
173 " foo",
174 " \"abc, def\"",
175 "pqr",
176 " \"mno\"",
177 "\"jkl\" ",
178 " \"ghi\"",
179 r#""abc,def""#,
180 ],
181 CommaSeparatedIterator::new(
182 r#""abc,def", "ghi","jkl" , "mno",pqr, "abc, def", foo, " foo", ',foo', "fo'o""#
183 )
184 .rev()
185 .collect::<Vec<&str>>()
186 );
187
188 let mut iter = CommaSeparatedIterator::new("a,b,c,d");
189 assert_eq!(Some("a"), iter.next());
190 assert_eq!(Some("d"), iter.next_back());
191 assert_eq!(Some("b"), iter.next());
192 assert_eq!(Some("c"), iter.next_back());
193 }
194}