1#![allow(non_upper_case_globals)]
4
5use crate::node::Attributes;
6use crate::parser::{Error, Reader, Result};
7
8#[derive(Clone, Debug)]
10pub struct Tag<'l>(pub &'l str, pub Type, pub Attributes);
11
12#[derive(Clone, Copy, Debug, Eq, PartialEq)]
16pub enum Type {
17 Start,
19 End,
21 Empty,
23}
24
25struct Parser<'l> {
26 reader: Reader<'l>,
27}
28
29impl<'l> Tag<'l> {
30 #[inline]
32 pub fn parse(content: &'l str) -> Result<Tag<'l>> {
33 Parser::new(content).process()
34 }
35}
36
37macro_rules! raise(
38 ($parser:expr, $($argument:tt)*) => (
39 return Err(Error::new($parser.reader.position(), format!($($argument)*)))
40 );
41);
42
43impl<'l> Parser<'l> {
44 #[inline]
45 fn new(content: &'l str) -> Self {
46 Parser {
47 reader: Reader::new(content),
48 }
49 }
50
51 fn process(&mut self) -> Result<Tag<'l>> {
52 if self.reader.consume_char('/') {
53 self.read_end_tag()
54 } else {
55 self.read_start_or_empty_tag()
56 }
57 }
58
59 fn read_attribute(&mut self) -> Result<Option<(String, String)>> {
60 let attribute = self
61 .reader
62 .capture(|reader| reader.consume_attribute())
63 .map(String::from);
64 match attribute {
65 Some(attribute) => {
66 let k = attribute.find('=').unwrap();
67 let name = attribute[0..k].trim_end();
68 let value = attribute[(k + 1)..].trim_start();
69 let value = &value[1..(value.len() - 1)];
70 Ok(Some((String::from(name), String::from(value))))
71 }
72 _ => Ok(None),
73 }
74 }
75
76 fn read_attributes(&mut self) -> Result<Attributes> {
77 let mut attributes = Attributes::new();
78 loop {
79 self.reader.consume_whitespace();
80 match self.read_attribute()? {
81 Some((name, value)) => {
82 attributes.insert(name, value.into());
83 }
84 _ => break,
85 }
86 }
87 Ok(attributes)
88 }
89
90 fn read_end_tag(&mut self) -> Result<Tag<'l>> {
91 let name = self.read_name()?;
92 self.reader.consume_whitespace();
93 if !self.reader.is_done() {
94 raise!(self, "found an end tag with excessive data");
95 }
96 Ok(Tag(name, Type::End, Attributes::default()))
97 }
98
99 fn read_name(&mut self) -> Result<&'l str> {
100 let name = self.reader.capture(|reader| reader.consume_name());
101 match name {
102 Some(name) => Ok(name),
103 _ => raise!(self, "expected a name"),
104 }
105 }
106
107 fn read_start_or_empty_tag(&mut self) -> Result<Tag<'l>> {
108 let name = self.read_name()?;
109 let attributes = self.read_attributes()?;
110 self.reader.consume_whitespace();
111 let tail = self.reader.capture(|reader| reader.consume_all());
112 match tail {
113 Some("/") => Ok(Tag(name, Type::Empty, attributes)),
114 Some(_) => raise!(self, "found an unexpected ending of a tag"),
115 _ => Ok(Tag(name, Type::Start, attributes)),
116 }
117 }
118}
119
120macro_rules! implement {
121 ($($const_name:ident: $tag_name:expr,)*) => ($(
122 #[doc = $tag_name]
123 pub const $const_name: &'static str = $tag_name;
124 )*);
125}
126
127implement! {
128 Anchor: "a",
129 Animate: "animate",
130 AnimateColor: "animateColor",
131 AnimateMotion: "animateMotion",
132 AnimateTransform: "animateTransform",
133 Circle: "circle",
134 ClipPath: "clipPath",
135 Definitions: "defs",
136 Description: "desc",
137 Ellipse: "ellipse",
138 Filter: "filter",
139 FilterEffectBlend: "feBlend",
140 FilterEffectColorMatrix: "feColorMatrix",
141 FilterEffectComponentTransfer: "feComponentTransfer",
142 FilterEffectComposite: "feComposite",
143 FilterEffectConvolveMatrix: "feConvolveMatrix",
144 FilterEffectDiffuseLighting: "feDiffuseLighting",
145 FilterEffectDisplacementMap: "feDisplacementMap",
146 FilterEffectDistantLight: "feDistantLight",
147 FilterEffectDropShadow: "feDropShadow",
148 FilterEffectFlood: "feFlood",
149 FilterEffectFunctionA: "feFuncA",
150 FilterEffectFunctionB: "feFuncB",
151 FilterEffectFunctionG: "feFuncG",
152 FilterEffectFunctionR: "feFuncR",
153 FilterEffectGaussianBlur: "feGaussianBlur",
154 FilterEffectImage: "feImage",
155 FilterEffectMerge: "feMerge",
156 FilterEffectMergeNode: "feMergeNode",
157 FilterEffectMorphology: "feMorphology",
158 FilterEffectOffset: "feOffset",
159 FilterEffectPointLight: "fePointLight",
160 FilterEffectSpecularLighting: "feSpecularLighting",
161 FilterEffectSpotLight: "feSpotLight",
162 FilterEffectTile: "feTile",
163 FilterEffectTurbulence: "feTurbulence",
164 ForeignObject: "foreignObject",
165 Group: "g",
166 Image: "image",
167 Line: "line",
168 LinearGradient: "linearGradient",
169 Link: "link",
170 Marker: "marker",
171 Mask: "mask",
172 MotionPath: "mpath",
173 Path: "path",
174 Pattern: "pattern",
175 Polygon: "polygon",
176 Polyline: "polyline",
177 RadialGradient: "radialGradient",
178 Rectangle: "rect",
179 Script: "script",
180 Stop: "stop",
181 Style: "style",
182 SVG: "svg",
183 Symbol: "symbol",
184 Text: "text",
185 TextPath: "textPath",
186 Title: "title",
187 TSpan: "tspan",
188 Use: "use",
189}
190
191#[cfg(test)]
192mod tests {
193 use super::{Parser, Tag, Type};
194
195 #[test]
196 fn parser_process() {
197 macro_rules! test(
198 ($content:expr, $kind:ident) => ({
199 let mut parser = Parser::new($content);
200 match parser.process().unwrap() {
201 Tag("foo", Type::$kind, _) => {}
202 _ => unreachable!(),
203 }
204 });
205 );
206
207 test!("foo", Start);
208 test!("foo ", Start);
209 test!("/foo", End);
210 test!("/foo ", End);
211 test!("foo/", Empty);
212 test!("foo /", Empty);
213 }
214
215 #[test]
216 fn parser_read_attribute() {
217 macro_rules! test(
218 ($content:expr, $name:expr, $value:expr) => ({
219 let mut parser = Parser::new($content);
220 let (name, value) = parser.read_attribute().unwrap().unwrap();
221 assert_eq!(&*name, $name);
222 assert_eq!(&*value, $value);
223 });
224 );
225
226 test!("foo=''", "foo", "");
227 test!("foo='bar'", "foo", "bar");
228 test!("foo =\"bar\"", "foo", "bar");
229 test!("foo= \"bar\"", "foo", "bar");
230 test!("foo\t=\n'bar' ", "foo", "bar");
231 test!("標籤='數值'", "標籤", "數值");
232 }
233}