use Error;
use reader::Reader;
use tag::Tag;
pub struct Parser<'s> {
reader: Reader<'s>,
}
pub enum Event {
Error(Error),
Comment,
Declaration,
Instruction,
Tag(Tag),
}
impl<'s> Parser<'s> {
pub fn new(text: &'s str) -> Parser<'s> {
Parser {
reader: Reader::new(text),
}
}
}
macro_rules! raise(
($parser:expr, $($arg:tt)*) => ({
let (line, column) = $parser.reader.position();
return Some(Event::Error(Error {
line: line,
column: column,
message: format!($($arg)*),
}))
});
);
impl<'s> Iterator for Parser<'s> {
type Item = Event;
fn next(&mut self) -> Option<Event> {
self.reader.consume_until_char('<');
if !self.reader.consume_char('<') {
return None;
}
let content = self.reader.capture(|reader| {
reader.consume_until_char('>');
}).and_then(|content| Some(String::from_str(content)));
if content.is_none() {
return raise!(self, "found an empty tag");
}
if !self.reader.consume_char('>') {
raise!(self, "missing a closing angle bracket");
}
let content = &(content.unwrap());
Some(if content.starts_with("!--") {
Event::Comment
} else if content.starts_with("!") {
Event::Declaration
} else if content.starts_with("?") {
Event::Instruction
} else {
match Tag::parse(content) {
Ok(tag) => Event::Tag(tag),
Err(error) => Event::Error(error),
}
})
}
}
#[cfg(test)]
mod tests {
use super::{Event, Parser};
use tag::Tag;
#[test]
fn next() {
macro_rules! test(
($text:expr, $name:expr) => ({
let mut parser = Parser::new($text);
match parser.next().unwrap() {
Event::Tag(Tag::Unknown(name, _, _)) => assert_eq!(&*name, $name),
_ => assert!(false),
}
})
);
test!("<foo>", "foo");
test!("<foo/>", "foo");
test!(" <foo/>", "foo");
test!("foo <bar>", "bar");
test!("foo> <bar>", "bar");
}
}