use std::io::{self, BufReader, Read};
use std::iter::Peekable;
use super::{entry, Entry, Lines};
pub struct Iter<R: Read> {
input: Peekable<Lines<BufReader<R>>>,
state: State,
}
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
enum State {
Begin,
Header,
Body,
}
impl<R: Read> Iter<R> {
#[inline]
pub fn new(input: R) -> Self {
Iter {
input: super::lines(input).peekable(),
state: State::Begin,
}
}
}
impl<R: Read> Iterator for Iter<R> {
type Item = io::Result<Entry>;
fn next(&mut self) -> Option<Self::Item> {
macro_rules! eof {
($body:expr) => (
if let Some(value) = $body {
value
}
else {
if self.state == State::Body {
self.state = State::Begin;
return Some(Ok(Entry::End));
}
return None;
}
);
}
macro_rules! try {
($body:expr) => (
match $body {
Ok(value) =>
value,
Err(err) =>
return Some(Err(err.into()))
}
);
}
macro_rules! utf8 {
($body:expr) => (
match $body {
Ok(value) =>
value,
Err(_) =>
return Some(Err(io::Error::new(io::ErrorKind::InvalidData, "stream did not contain valid UTF-8")))
}
);
}
loop {
let (offset, line) = try!(eof!(self.input.next()));
match self.state {
State::Begin => {
let value = try!(entry::Begin::new(utf8!(String::from_utf8(line))));
self.state = State::Header;
return Some(Ok(Entry::Begin(offset, value)));
}
State::Header => {
if line.is_empty() {
self.state = State::Body;
continue;
}
if line[0] == b'>' {
continue;
}
let mut line = line;
loop {
let consumed;
if let Ok((_, ref current)) = *eof!(self.input.peek()) {
match current.first() {
Some(&b' ') | Some(&b'\t') => {
line.extend_from_slice(current);
consumed = true;
}
_ => break
}
}
else {
break;
}
if consumed {
self.input.next();
}
}
return Some(Ok(Entry::Header(try!(entry::Header::new(line)))));
}
State::Body => {
if line.is_empty() {
if let Ok((_, ref current)) = *eof!(self.input.peek()) {
if entry::Begin::ranges(current).is_ok() {
self.state = State::Begin;
return Some(Ok(Entry::End));
}
}
}
return Some(Ok(Entry::Body(line)));
}
}
}
}
}
#[cfg(test)]
mod test {
use std::io::Cursor;
use super::*;
use super::super::Entry;
#[test]
fn simple() {
let mut iter = Iter::new(Cursor::new("From meh@schizofreni.co Wed Nov 17 14:35:53 2010\r\nSubject: I like trains\r\nFoo: bar\r\n baz\r\n\r\nHi!\r\n"));
{
if let Entry::Begin(_, item) = iter.next().unwrap().unwrap() {
assert_eq!(item.address(), "meh@schizofreni.co");
assert_eq!(item.timestamp(), "Wed Nov 17 14:35:53 2010");
}
else {
assert!(false);
}
}
{
if let Entry::Header(item) = iter.next().unwrap().unwrap() {
assert_eq!(&*item.key(), "Subject");
assert_eq!(&*item.value(), "I like trains");
}
else {
assert!(false);
}
}
{
if let Entry::Header(item) = iter.next().unwrap().unwrap() {
assert_eq!(&*item.key(), "Foo");
assert_eq!(&*item.value(), "bar baz");
}
else {
assert!(false);
}
}
}
}