use std::str::Chars;
use std::iter::Iterator;
use std::cmp::{ PartialEq };
use error::CoreError;
use spec::{GeneralQSSpec, ScanAutomaton, PartialCodePoint};
#[allow(warnings)]
use std::ascii::AsciiExt;
pub trait AsciiCaseInsensitiveEq<Rhs: ?Sized> {
fn eq_ignore_ascii_case(&self, other: &Rhs) -> bool;
}
#[derive(Debug, Clone)]
pub struct ContentChars<'a, Impl: GeneralQSSpec> {
inner: Chars<'a>,
automaton: ScanAutomaton<Impl::Parsing>
}
impl<'s, Impl> ContentChars<'s, Impl>
where Impl: GeneralQSSpec
{
pub fn from_str(quoted: &'s str) -> Self {
ContentChars {
inner: quoted.chars(),
automaton: ScanAutomaton::<Impl::Parsing>::new()
}
}
pub fn from_parts_unchecked(
partial_quoted_content: &'s str,
automaton: ScanAutomaton<Impl::Parsing>
) -> Self
{
let inner = partial_quoted_content.chars();
ContentChars{ inner, automaton }
}
}
impl<'a, Impl> Iterator for ContentChars<'a, Impl>
where Impl: GeneralQSSpec
{
type Item = Result<char, CoreError>;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(ch) = self.inner.next() {
let res = self.automaton.advance(PartialCodePoint::from_code_point(ch as u32));
match res {
Err(e) => return Some(Err(e.into())),
Ok(true) => return Some(Ok(ch)),
Ok(false) => {},
}
} else {
match self.automaton.end() {
Err(e) => return Some(Err(e.into())),
Ok(()) => return None
}
}
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<'a, Spec> PartialEq<str> for ContentChars<'a, Spec>
where Spec: GeneralQSSpec
{
#[inline]
fn eq(&self, other: &str) -> bool {
iter_eq(self.clone(), other.chars().map(|ch|Ok(ch)), |l,r|l==r)
}
}
impl<'a, 'b, Spec> PartialEq<ContentChars<'b, Spec>> for &'a str
where Spec: GeneralQSSpec
{
#[inline]
fn eq(&self, other: &ContentChars<'b, Spec>) -> bool {
*other == **self
}
}
impl<'a, 'b, Spec> PartialEq<&'b str> for ContentChars<'a, Spec>
where Spec: GeneralQSSpec
{
#[inline]
fn eq(&self, other: &&'b str) -> bool {
self == *other
}
}
impl<'a, 'b, Spec> PartialEq<ContentChars<'b, Spec>> for ContentChars<'a, Spec>
where Spec: GeneralQSSpec
{
#[inline]
fn eq(&self, other: &ContentChars<'b, Spec>) -> bool {
iter_eq(self.clone(), other.clone(), |l,r|l==r)
}
}
impl<'a, Spec> AsciiCaseInsensitiveEq<str> for ContentChars<'a, Spec>
where Spec: GeneralQSSpec
{
#[inline]
fn eq_ignore_ascii_case(&self, other: &str) -> bool {
iter_eq(self.clone(), other.chars().map(|ch|Ok(ch)), |l,r| l.eq_ignore_ascii_case(&r))
}
}
impl<'a, 'b, Spec> AsciiCaseInsensitiveEq<ContentChars<'b, Spec>> for ContentChars<'a, Spec>
where Spec: GeneralQSSpec
{
#[inline]
fn eq_ignore_ascii_case(&self, other: &ContentChars<'b, Spec>) -> bool {
iter_eq(self.clone(), other.clone(), |l,r|l.eq_ignore_ascii_case(&r))
}
}
impl<'a, 'b, Spec> AsciiCaseInsensitiveEq<ContentChars<'b, Spec>> for &'a str
where Spec: GeneralQSSpec
{
#[inline]
fn eq_ignore_ascii_case(&self, other: &ContentChars<'b, Spec>) -> bool {
other == *self
}
}
impl<'a, 'b, Spec> AsciiCaseInsensitiveEq<&'b str> for ContentChars<'a, Spec>
where Spec: GeneralQSSpec
{
#[inline]
fn eq_ignore_ascii_case(&self, other: &&'b str) -> bool {
self == *other
}
}
fn iter_eq<I1, I2, E, FN>(mut left: I1, mut right: I2, cmp: FN) -> bool
where I1: Iterator<Item=Result<char, E>>,
I2: Iterator<Item=Result<char, E>>, FN: Fn(char, char) -> bool
{
loop {
match (left.next(), right.next()) {
(None, None) => return true,
(Some(Ok(x)), Some(Ok(y))) if cmp(x, y) => (),
_ => return false
}
}
}
#[cfg(test)]
mod test {
use test_utils::*;
use error::CoreError;
use super::{ContentChars, AsciiCaseInsensitiveEq};
#[test]
fn missing_double_quoted() {
let mut chars = ContentChars::<TestSpec>::from_str("abcdef");
assert_eq!(chars.next().expect("is some").unwrap_err(), CoreError::DoesNotStartWithDQuotes);
}
#[test]
fn unnecessary_quoted() {
let res = ContentChars::<TestSpec>::from_str("\"abcdef\"");
assert_eq!(res.collect::<Result<Vec<_>, _>>().unwrap().as_slice(), &[
'a', 'b', 'c' ,'d', 'e', 'f'
])
}
#[test]
fn quoted() {
let res = ContentChars::<TestSpec>::from_str("\"abc def\"");
assert_eq!(res.collect::<Result<Vec<_>, _>>().unwrap().as_slice(), &[
'a', 'b', 'c', ' ', 'd', 'e', 'f'
])
}
#[test]
fn with_quoted_pair() {
let res = ContentChars::<TestSpec>::from_str(r#""abc\" \def""#);
assert_eq!(res.collect::<Result<Vec<_>, _>>().unwrap().as_slice(), &[
'a', 'b', 'c', '"', ' ', 'd', 'e', 'f'
])
}
#[test]
fn strip_non_semantic_ws() {
let res = ContentChars::<TestSpec>::from_str("\"abc\n\ndef\"");
assert_eq!(res.collect::<Result<Vec<_>, _>>().unwrap().as_slice(), &[
'a', 'b', 'c', 'd', 'e', 'f'
])
}
#[test]
fn ascii_case_insensitive_eq() {
let left = ContentChars::<TestSpec>::from_str(r#""abc""#);
let right = ContentChars::<TestSpec>::from_str(r#""aBc""#);
assert!(left.eq_ignore_ascii_case(&right))
}
}