use node::Attributes;
use parser::{Error, Reader, Result};
pub struct Tag<'l>(pub &'l str, pub Type, pub Attributes);
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Type {
Start,
End,
Empty,
}
struct Parser<'l> {
reader: Reader<'l>,
}
impl<'l> Tag<'l> {
#[inline]
pub fn parse(content: &'l str) -> Result<Tag<'l>> {
Parser::new(content).process()
}
}
macro_rules! raise(
($parser:expr, $($argument:tt)*) => (
return Err(Error::new($parser.reader.position(), format!($($argument)*)));
);
);
impl<'l> Parser<'l> {
#[inline]
fn new(content: &'l str) -> Self {
Parser { reader: Reader::new(content) }
}
fn process(&mut self) -> Result<Tag<'l>> {
if self.reader.consume_char('/') {
self.read_end_tag()
} else {
self.read_start_or_empty_tag()
}
}
fn read_attribute(&mut self) -> Result<Option<(String, String)>> {
let attribute = self.reader.capture(|reader| {
reader.consume_attribute();
}).and_then(|attribute| Some(String::from(attribute)));
match attribute {
Some(attribute) => {
let k = (&attribute).find('=').unwrap();
let name = (&attribute[0..k]).trim_right();
let value = (&attribute[(k+1)..]).trim_left();
let value = &value[1..(value.len()-1)];
Ok(Some((String::from(name), String::from(value))))
},
_ => Ok(None),
}
}
fn read_attributes(&mut self) -> Result<Attributes> {
let mut attributes = Attributes::new();
loop {
self.reader.consume_whitespace();
match try!(self.read_attribute()) {
Some((name, value)) => {
attributes.insert(name, value.into());
},
_ => break,
}
}
Ok(attributes)
}
fn read_end_tag(&mut self) -> Result<Tag<'l>> {
let name = try!(self.read_name());
self.reader.consume_whitespace();
if !self.reader.is_done() {
raise!(self, "found an end tag with excessive data");
}
Ok(Tag(name, Type::End, Attributes::default()))
}
fn read_name(&mut self) -> Result<&'l str> {
let name = self.reader.capture(|reader| {
reader.consume_name();
});
match name {
Some(name) => Ok(name),
_ => raise!(self, "expected a name"),
}
}
fn read_start_or_empty_tag(&mut self) -> Result<Tag<'l>> {
let name = try!(self.read_name());
let attributes = try!(self.read_attributes());
self.reader.consume_whitespace();
let tail = self.reader.capture(|reader| {
reader.consume_all();
});
match tail {
Some("/") => Ok(Tag(name, Type::Empty, attributes)),
Some(_) => raise!(self, "found an unexpected ending of a tag"),
_ => Ok(Tag(name, Type::Start, attributes)),
}
}
}
#[cfg(test)]
mod tests {
use super::{Parser, Tag, Type};
#[test]
fn parser_process() {
macro_rules! test(
($content:expr, $kind:ident) => ({
let mut parser = Parser::new($content);
match parser.process().unwrap() {
Tag("foo", Type::$kind, _) => {},
_ => unreachable!(),
}
});
);
test!("foo", Start);
test!("foo ", Start);
test!("/foo", End);
test!("/foo ", End);
test!("foo/", Empty);
test!("foo /", Empty);
}
#[test]
fn parser_read_attribute() {
macro_rules! test(
($content:expr, $name:expr, $value:expr) => ({
let mut parser = Parser::new($content);
let (name, value) = parser.read_attribute().unwrap().unwrap();
assert_eq!(&*name, $name);
assert_eq!(&*value, $value);
});
);
test!("foo='bar'", "foo", "bar");
test!("foo =\"bar\"", "foo", "bar");
test!("foo= \"bar\"", "foo", "bar");
test!("foo\t=\n'bar' ", "foo", "bar");
}
}