svg/parser/
mod.rs

1//! The parser.
2
3use crate::node::element::tag::{Tag, Type};
4use crate::node::Attributes;
5
6mod error;
7mod reader;
8
9pub use self::error::Error;
10
11#[doc(hidden)]
12pub use self::reader::Reader;
13
14/// A parser.
15pub struct Parser<'l> {
16    reader: Reader<'l>,
17}
18
19/// An event.
20#[derive(Debug)]
21pub enum Event<'l> {
22    /// An error.
23    Error(Error),
24    /// A tag.
25    Tag(&'l str, Type, Attributes),
26    /// A text.
27    Text(&'l str),
28    /// A comment.
29    Comment(&'l str),
30    /// A declaration.
31    Declaration(&'l str),
32    /// An instruction.
33    Instruction(&'l str),
34}
35
36/// A result.
37pub type Result<T> = std::result::Result<T, Error>;
38
39macro_rules! raise(
40    ($parser:expr, $($argument:tt)*) => (
41        return Some(Event::Error(Error::new($parser.reader.position(), format!($($argument)*))))
42    );
43);
44
45impl<'l> Parser<'l> {
46    /// Create a parser.
47    #[inline]
48    pub fn new(content: &'l str) -> Self {
49        Parser {
50            reader: Reader::new(content),
51        }
52    }
53
54    fn next_angle(&mut self) -> Option<Event<'l>> {
55        let content: String = self.reader.peek_many().take(4).collect();
56        if content.is_empty() {
57            None
58        } else if content.starts_with("<!--") {
59            self.read_comment()
60        } else if content.starts_with("<!") {
61            self.read_declaration()
62        } else if content.starts_with("<?") {
63            self.read_instruction()
64        } else if content.starts_with('<') {
65            self.read_tag()
66        } else {
67            raise!(self, "found an unknown sequence");
68        }
69    }
70
71    fn next_text(&mut self) -> Option<Event<'l>> {
72        self.reader
73            .capture(|reader| reader.consume_until_char('<'))
74            .map(Event::Text)
75    }
76
77    fn read_comment(&mut self) -> Option<Event<'l>> {
78        match self.reader.capture(|reader| reader.consume_comment()) {
79            None => raise!(self, "found a malformed comment"),
80            Some(content) => Some(Event::Comment(content)),
81        }
82    }
83
84    fn read_declaration(&mut self) -> Option<Event<'l>> {
85        match self.reader.capture(|reader| reader.consume_declaration()) {
86            None => raise!(self, "found a malformed declaration"),
87            Some(content) => Some(Event::Declaration(content)),
88        }
89    }
90
91    fn read_instruction(&mut self) -> Option<Event<'l>> {
92        match self.reader.capture(|reader| reader.consume_instruction()) {
93            None => raise!(self, "found a malformed instruction"),
94            Some(content) => Some(Event::Instruction(content)),
95        }
96    }
97
98    fn read_tag(&mut self) -> Option<Event<'l>> {
99        match self.reader.capture(|reader| reader.consume_tag()) {
100            None => raise!(self, "found a malformed tag"),
101            Some(content) => Some(match Tag::parse(&content[1..content.len() - 1]) {
102                Ok(Tag(name, kind, attributes)) => Event::Tag(name, kind, attributes),
103                Err(error) => Event::Error(error),
104            }),
105        }
106    }
107}
108
109impl<'l> Iterator for Parser<'l> {
110    type Item = Event<'l>;
111
112    fn next(&mut self) -> Option<Self::Item> {
113        self.next_text().or_else(|| self.next_angle())
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use crate::parser::{Event, Parser};
120
121    #[test]
122    fn next_tag() {
123        macro_rules! test(
124            ($content:expr, $value:expr) => ({
125                let mut parser = Parser::new($content);
126                match parser.next().unwrap() {
127                    Event::Tag(value, _, _) => assert_eq!(value, $value),
128                    _ => unreachable!(),
129                }
130            })
131        );
132
133        test!("<foo>", "foo");
134        test!("<foo/>", "foo");
135        test!("  <foo/>", "foo");
136    }
137
138    #[test]
139    fn next_text() {
140        macro_rules! test(
141            ($content:expr, $value:expr) => ({
142                let mut parser = Parser::new($content);
143                match parser.next().unwrap() {
144                    Event::Text(value) => assert_eq!(value, $value),
145                    _ => unreachable!(),
146                }
147            })
148        );
149
150        test!("foo <bar>", "foo");
151        test!("  foo<bar>", "foo");
152        test!("foo> <bar>", "foo>");
153    }
154}