1use std::fmt;
14use std::str;
15
16use xmlparser::{
17 self,
18 FromSpan,
19 Reference,
20 Stream,
21 StrSpan,
22};
23
24use error::{
25 StreamError,
26 StreamResult,
27};
28use {
29 AttributeId,
30};
31
32#[derive(PartialEq)]
34pub enum Token<'a> {
35 XmlAttribute(&'a str, &'a str),
37 SvgAttribute(AttributeId, StrSpan<'a>),
39 EntityRef(&'a str),
41}
42
43impl<'a> fmt::Debug for Token<'a> {
44 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
45 match *self {
46 Token::XmlAttribute(name, ref value) =>
47 write!(f, "XmlAttribute({}, {})", name, value),
48 Token::SvgAttribute(id, ref value) =>
49 write!(f, "SvgAttribute({:?}, {:?})", id, value),
50 Token::EntityRef(name) =>
51 write!(f, "EntityRef({})", name),
52 }
53 }
54}
55
56#[derive(Clone, Copy, PartialEq)]
58pub struct Tokenizer<'a> {
59 stream: Stream<'a>,
60}
61
62impl<'a> FromSpan<'a> for Tokenizer<'a> {
63 fn from_span(span: StrSpan<'a>) -> Self {
64 Tokenizer {
65 stream: Stream::from_span(span)
66 }
67 }
68}
69
70impl<'a> fmt::Debug for Tokenizer<'a> {
71 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
72 write!(f, "StyleTokenizer({:?})", self.stream.span())
73 }
74}
75
76impl<'a> Iterator for Tokenizer<'a> {
77 type Item = StreamResult<Token<'a>>;
78
79 fn next(&mut self) -> Option<Self::Item> {
93 self.stream.skip_spaces();
94
95 if self.stream.at_end() {
96 return None;
97 }
98
99 macro_rules! try2 {
100 ($expr:expr) => {
101 match $expr {
102 Ok(value) => value,
103 Err(e) => {
104 return Some(Err(e.into()));
105 }
106 }
107 }
108 }
109
110 let c = try2!(self.stream.curr_byte());
111 if c == b'/' {
112 try2!(skip_comment(&mut self.stream));
113 self.next()
114 } else if c == b'-' {
115 try2!(parse_prefix(&mut self.stream));
116 self.next()
117 } else if c == b'&' {
118 Some(parse_entity_ref(&mut self.stream))
119 } else if is_ident_char(c) {
120 Some(parse_attribute(&mut self.stream))
121 } else {
122 let pos = self.stream.gen_error_pos();
123 self.stream.jump_to_end();
124 Some(Err(xmlparser::StreamError::InvalidChar(c as char, "/-&".into(), pos).into()))
125 }
126 }
127}
128
129fn skip_comment(stream: &mut Stream) -> StreamResult<()> {
130 stream.skip_string(b"/*")?;
131 stream.skip_bytes(|_, c| c != b'*');
132 stream.skip_string(b"*/")?;
133 stream.skip_spaces();
134
135 Ok(())
136}
137
138fn parse_attribute<'a>(stream: &mut Stream<'a>) -> StreamResult<Token<'a>> {
139 let name = stream.consume_bytes(|_, c| is_ident_char(c));
140
141 if name.is_empty() {
142 return Err(xmlparser::StreamError::UnexpectedEndOfStream.into());
145 }
146
147 stream.skip_spaces();
148 stream.consume_byte(b':')?;
149 stream.skip_spaces();
150
151 let value = if stream.curr_byte()? == b'\'' {
152 stream.advance(1);
153 let v = stream.consume_bytes(|_, c| c != b'\'');
154 stream.consume_byte(b'\'')?;
155 v
156 } else if stream.starts_with(b"'") {
157 stream.advance(6);
158 let v = stream.consume_bytes(|_, c| c != b'&');
159 stream.skip_string(b"'")?;
160 v
161 } else {
162 stream.consume_bytes(|_, c| c != b';' && c != b'/')
163 }.trim();
164
165 if value.len() == 0 {
166 return Err(xmlparser::StreamError::UnexpectedEndOfStream.into());
167 }
168
169 stream.skip_spaces();
170
171 while stream.is_curr_byte_eq(b';') {
173 stream.advance(1);
174 stream.skip_spaces();
175 }
176
177 if let Some(aid) = AttributeId::from_name(name.to_str()) {
178 Ok(Token::SvgAttribute(aid, value))
179 } else {
180 Ok(Token::XmlAttribute(name.to_str(), value.to_str()))
181 }
182}
183
184fn parse_entity_ref<'a>(stream: &mut Stream<'a>) -> StreamResult<Token<'a>> {
185 match stream.consume_reference()? {
186 Reference::EntityRef(name) => {
187 Ok(Token::EntityRef(name.to_str()))
188 }
189 Reference::CharRef(_) => {
190 Err(StreamError::InvalidEntityRef(stream.gen_error_pos()))
191 }
192 }
193}
194
195fn parse_prefix<'a>(stream: &mut Stream<'a>) -> StreamResult<()> {
196 stream.advance(1); let t = parse_attribute(stream)?;
200
201 if let Token::XmlAttribute(name, _) = t {
202 warn!("Style attribute '-{}' is skipped.", name);
203 }
204
205 Ok(())
206}
207
208fn is_ident_char(c: u8) -> bool {
210 match c {
211 b'0'...b'9'
212 | b'A'...b'Z'
213 | b'a'...b'z'
214 | b'-'
215 | b'_' => true,
216 _ => false,
217 }
218}