1use std::str::Chars;
2use std::iter::Iterator;
3use std::cmp::{ PartialEq };
4
5use error::CoreError;
6use spec::{GeneralQSSpec, ScanAutomaton, PartialCodePoint};
7#[allow(warnings)]
11use std::ascii::AsciiExt;
12
13pub trait AsciiCaseInsensitiveEq<Rhs: ?Sized> {
15
16 fn eq_ignore_ascii_case(&self, other: &Rhs) -> bool;
23}
24
25#[derive(Debug, Clone)]
59pub struct ContentChars<'a, Impl: GeneralQSSpec> {
60 inner: Chars<'a>,
61 automaton: ScanAutomaton<Impl::Parsing>
62}
63
64impl<'s, Impl> ContentChars<'s, Impl>
65 where Impl: GeneralQSSpec
66{
67
68 pub fn from_str(quoted: &'s str) -> Self {
73 ContentChars {
74 inner: quoted.chars(),
75 automaton: ScanAutomaton::<Impl::Parsing>::new()
76 }
77 }
78
79 pub fn from_parts_unchecked(
89 partial_quoted_content: &'s str,
90 automaton: ScanAutomaton<Impl::Parsing>
91 ) -> Self
92 {
93 let inner = partial_quoted_content.chars();
94 ContentChars{ inner, automaton }
95 }
96}
97
98
99impl<'a, Impl> Iterator for ContentChars<'a, Impl>
100 where Impl: GeneralQSSpec
101{
102 type Item = Result<char, CoreError>;
103
104 fn next(&mut self) -> Option<Self::Item> {
105 loop {
106 if let Some(ch) = self.inner.next() {
107 let res = self.automaton.advance(PartialCodePoint::from_code_point(ch as u32));
108 match res {
109 Err(e) => return Some(Err(e.into())),
110 Ok(true) => return Some(Ok(ch)),
111 Ok(false) => {},
112 }
113 } else {
114 match self.automaton.end() {
115 Err(e) => return Some(Err(e.into())),
116 Ok(()) => return None
117 }
118 }
119 }
120 }
121
122 #[inline]
123 fn size_hint(&self) -> (usize, Option<usize>) {
124 self.inner.size_hint()
125 }
126}
127
128
129impl<'a, Spec> PartialEq<str> for ContentChars<'a, Spec>
130 where Spec: GeneralQSSpec
131{
132
133 #[inline]
134 fn eq(&self, other: &str) -> bool {
135 iter_eq(self.clone(), other.chars().map(|ch|Ok(ch)), |l,r|l==r)
136 }
137}
138
139impl<'a, 'b, Spec> PartialEq<ContentChars<'b, Spec>> for &'a str
140 where Spec: GeneralQSSpec
141{
142 #[inline]
143 fn eq(&self, other: &ContentChars<'b, Spec>) -> bool {
144 *other == **self
145 }
146}
147
148impl<'a, 'b, Spec> PartialEq<&'b str> for ContentChars<'a, Spec>
149 where Spec: GeneralQSSpec
150{
151 #[inline]
152 fn eq(&self, other: &&'b str) -> bool {
153 self == *other
154 }
155}
156
157impl<'a, 'b, Spec> PartialEq<ContentChars<'b, Spec>> for ContentChars<'a, Spec>
158 where Spec: GeneralQSSpec
159{
160 #[inline]
161 fn eq(&self, other: &ContentChars<'b, Spec>) -> bool {
162 iter_eq(self.clone(), other.clone(), |l,r|l==r)
163 }
164}
165
166
167
168impl<'a, Spec> AsciiCaseInsensitiveEq<str> for ContentChars<'a, Spec>
169 where Spec: GeneralQSSpec
170{
171 #[inline]
172 fn eq_ignore_ascii_case(&self, other: &str) -> bool {
173 iter_eq(self.clone(), other.chars().map(|ch|Ok(ch)), |l,r| l.eq_ignore_ascii_case(&r))
174 }
175}
176
177impl<'a, 'b, Spec> AsciiCaseInsensitiveEq<ContentChars<'b, Spec>> for ContentChars<'a, Spec>
178 where Spec: GeneralQSSpec
179{
180 #[inline]
181 fn eq_ignore_ascii_case(&self, other: &ContentChars<'b, Spec>) -> bool {
182 iter_eq(self.clone(), other.clone(), |l,r|l.eq_ignore_ascii_case(&r))
183 }
184}
185
186impl<'a, 'b, Spec> AsciiCaseInsensitiveEq<ContentChars<'b, Spec>> for &'a str
187 where Spec: GeneralQSSpec
188{
189 #[inline]
190 fn eq_ignore_ascii_case(&self, other: &ContentChars<'b, Spec>) -> bool {
191 other == *self
192 }
193}
194
195
196
197impl<'a, 'b, Spec> AsciiCaseInsensitiveEq<&'b str> for ContentChars<'a, Spec>
198 where Spec: GeneralQSSpec
199{
200 #[inline]
201 fn eq_ignore_ascii_case(&self, other: &&'b str) -> bool {
202 self == *other
203 }
204}
205
206fn iter_eq<I1, I2, E, FN>(mut left: I1, mut right: I2, cmp: FN) -> bool
207 where I1: Iterator<Item=Result<char, E>>,
208 I2: Iterator<Item=Result<char, E>>, FN: Fn(char, char) -> bool
209{
210 loop {
211 match (left.next(), right.next()) {
212 (None, None) => return true,
213 (Some(Ok(x)), Some(Ok(y))) if cmp(x, y) => (),
214 _ => return false
215 }
216 }
217}
218
219
220
221#[cfg(test)]
222mod test {
223 use test_utils::*;
224 use error::CoreError;
225 use super::{ContentChars, AsciiCaseInsensitiveEq};
226
227 #[test]
228 fn missing_double_quoted() {
229 let mut chars = ContentChars::<TestSpec>::from_str("abcdef");
230 assert_eq!(chars.next().expect("is some").unwrap_err(), CoreError::DoesNotStartWithDQuotes);
231 }
232
233 #[test]
234 fn unnecessary_quoted() {
235 let res = ContentChars::<TestSpec>::from_str("\"abcdef\"");
236 assert_eq!(res.collect::<Result<Vec<_>, _>>().unwrap().as_slice(), &[
237 'a', 'b', 'c' ,'d', 'e', 'f'
238 ])
239 }
240
241 #[test]
242 fn quoted() {
243 let res = ContentChars::<TestSpec>::from_str("\"abc def\"");
244 assert_eq!(res.collect::<Result<Vec<_>, _>>().unwrap().as_slice(), &[
245 'a', 'b', 'c', ' ', 'd', 'e', 'f'
246 ])
247 }
248
249 #[test]
250 fn with_quoted_pair() {
251 let res = ContentChars::<TestSpec>::from_str(r#""abc\" \def""#);
252 assert_eq!(res.collect::<Result<Vec<_>, _>>().unwrap().as_slice(), &[
253 'a', 'b', 'c', '"', ' ', 'd', 'e', 'f'
254 ])
255 }
256
257 #[test]
258 fn strip_non_semantic_ws() {
259 let res = ContentChars::<TestSpec>::from_str("\"abc\n\ndef\"");
260 assert_eq!(res.collect::<Result<Vec<_>, _>>().unwrap().as_slice(), &[
261 'a', 'b', 'c', 'd', 'e', 'f'
262 ])
263 }
264
265 #[test]
266 fn ascii_case_insensitive_eq() {
267 let left = ContentChars::<TestSpec>::from_str(r#""abc""#);
268 let right = ContentChars::<TestSpec>::from_str(r#""aBc""#);
269 assert!(left.eq_ignore_ascii_case(&right))
270 }
271}