1// Copyright (c) 2017 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7macro_rules! get_attr {
8 ($elem:ident, $attr:tt, $type:tt) => (
9 get_attr!($elem, $attr, $type, value, value.parse()?)
10 );
11 ($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => (
12 match $elem.attr($attr) {
13 Some($value) => Some($func),
14 None => None,
15 }
16 );
17 ($elem:ident, $attr:tt, required, $value:ident, $func:expr) => (
18 match $elem.attr($attr) {
19 Some($value) => $func,
20 None => return Err(Error::ParseError(concat!("Required attribute '", $attr, "' missing."))),
21 }
22 );
23 ($elem:ident, $attr:tt, default, $value:ident, $func:expr) => (
24 match $elem.attr($attr) {
25 Some($value) => $func,
26 None => Default::default(),
27 }
28 );
29}
30
31macro_rules! generate_attribute {
32 ($elem:ident, $name:tt, {$($a:ident => $b:tt),+,}) => (
33 generate_attribute!($elem, $name, {$($a => $b),+});
34 );
35 ($elem:ident, $name:tt, {$($a:ident => $b:tt),+,}, Default = $default:ident) => (
36 generate_attribute!($elem, $name, {$($a => $b),+}, Default = $default);
37 );
38 ($elem:ident, $name:tt, {$($a:ident => $b:tt),+}) => (
39 #[derive(Debug, Clone, PartialEq)]
40 pub enum $elem {
41 $(
42 #[doc=$b]
43 #[doc="value for this attribute."]
44 $a
45 ),+
46 }
47 impl FromStr for $elem {
48 type Err = Error;
49 fn from_str(s: &str) -> Result<$elem, Error> {
50 Ok(match s {
51 $($b => $elem::$a),+,
52 _ => return Err(Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
53 })
54 }
55 }
56 impl IntoAttributeValue for $elem {
57 fn into_attribute_value(self) -> Option<String> {
58 Some(String::from(match self {
59 $($elem::$a => $b),+
60 }))
61 }
62 }
63 );
64 ($elem:ident, $name:tt, {$($a:ident => $b:tt),+}, Default = $default:ident) => (
65 #[derive(Debug, Clone, PartialEq)]
66 pub enum $elem {
67 $(
68 #[doc=$b]
69 #[doc="value for this attribute."]
70 $a
71 ),+
72 }
73 impl FromStr for $elem {
74 type Err = Error;
75 fn from_str(s: &str) -> Result<$elem, Error> {
76 Ok(match s {
77 $($b => $elem::$a),+,
78 _ => return Err(Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
79 })
80 }
81 }
82 impl IntoAttributeValue for $elem {
83 #[allow(unreachable_patterns)]
84 fn into_attribute_value(self) -> Option<String> {
85 Some(String::from(match self {
86 $elem::$default => return None,
87 $($elem::$a => $b),+
88 }))
89 }
90 }
91 impl Default for $elem {
92 fn default() -> $elem {
93 $elem::$default
94 }
95 }
96 );
97}
98
99macro_rules! generate_element_enum {
100 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+,}) => (
101 generate_element_enum!($(#[$meta])* $elem, $name, $ns, {$($(#[$enum_meta])* $enum => $enum_name),+});
102 );
103 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+}) => (
104 $(#[$meta])*
105 #[derive(Debug, Clone, PartialEq)]
106 pub enum $elem {
107 $(
108 $(#[$enum_meta])*
109 $enum
110 ),+
111 }
112 impl TryFrom<Element> for $elem {
113 type Err = Error;
114 fn try_from(elem: Element) -> Result<$elem, Error> {
115 check_ns_only!(elem, $name, $ns);
116 check_no_children!(elem, $name);
117 check_no_attributes!(elem, $name);
118 Ok(match elem.name() {
119 $($enum_name => $elem::$enum,)+
120 _ => return Err(Error::ParseError(concat!("This is not a ", $name, " element."))),
121 })
122 }
123 }
124 impl From<$elem> for Element {
125 fn from(elem: $elem) -> Element {
126 Element::builder(match elem {
127 $($elem::$enum => $enum_name,)+
128 }).ns($ns)
129 .build()
130 }
131 }
132 );
133}
134
135macro_rules! check_self {
136 ($elem:ident, $name:tt, $ns:expr) => (
137 check_self!($elem, $name, $ns, $name);
138 );
139 ($elem:ident, $name:tt, $ns:expr, $pretty_name:tt) => (
140 if !$elem.is($name, $ns) {
141 return Err(Error::ParseError(concat!("This is not a ", $pretty_name, " element.")));
142 }
143 );
144}
145
146macro_rules! check_ns_only {
147 ($elem:ident, $name:tt, $ns:expr) => (
148 if !$elem.has_ns($ns) {
149 return Err(Error::ParseError(concat!("This is not a ", $name, " element.")));
150 }
151 );
152}
153
154macro_rules! check_no_children {
155 ($elem:ident, $name:tt) => (
156 for _ in $elem.children() {
157 return Err(Error::ParseError(concat!("Unknown child in ", $name, " element.")));
158 }
159 );
160}
161
162macro_rules! check_no_attributes {
163 ($elem:ident, $name:tt) => (
164 check_no_unknown_attributes!($elem, $name, []);
165 );
166}
167
168macro_rules! check_no_unknown_attributes {
169 ($elem:ident, $name:tt, [$($attr:tt),*]) => (
170 for (_attr, _) in $elem.attrs() {
171 $(
172 if _attr == $attr {
173 continue;
174 }
175 )*
176 return Err(Error::ParseError(concat!("Unknown attribute in ", $name, " element.")));
177 }
178 );
179}
180
181macro_rules! generate_empty_element {
182 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr) => (
183 $(#[$meta])*
184 #[derive(Debug, Clone)]
185 pub struct $elem;
186
187 impl TryFrom<Element> for $elem {
188 type Err = Error;
189
190 fn try_from(elem: Element) -> Result<$elem, Error> {
191 check_self!(elem, $name, $ns);
192 check_no_children!(elem, $name);
193 check_no_attributes!(elem, $name);
194 Ok($elem)
195 }
196 }
197
198 impl From<$elem> for Element {
199 fn from(_: $elem) -> Element {
200 Element::builder($name)
201 .ns($ns)
202 .build()
203 }
204 }
205 );
206}
207
208macro_rules! generate_element_with_only_attributes {
209 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => (
210 generate_element_with_only_attributes!($(#[$meta])* $elem, $name, $ns, [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*]);
211 );
212 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => (
213 $(#[$meta])*
214 #[derive(Debug, Clone)]
215 pub struct $elem {
216 $(
217 $(#[$attr_meta])*
218 pub $attr: $attr_type,
219 )*
220 }
221
222 impl TryFrom<Element> for $elem {
223 type Err = Error;
224
225 fn try_from(elem: Element) -> Result<$elem, Error> {
226 check_self!(elem, $name, $ns);
227 check_no_children!(elem, $name);
228 check_no_unknown_attributes!(elem, $name, [$($attr_name),*]);
229 Ok($elem {
230 $(
231 $attr: get_attr!(elem, $attr_name, $attr_action),
232 )*
233 })
234 }
235 }
236
237 impl From<$elem> for Element {
238 fn from(elem: $elem) -> Element {
239 Element::builder($name)
240 .ns($ns)
241 $(
242 .attr($attr_name, elem.$attr)
243 )*
244 .build()
245 }
246 }
247 );
248}
249
250macro_rules! generate_id {
251 ($elem:ident) => (
252 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
253 pub struct $elem(pub String);
254 impl FromStr for $elem {
255 type Err = Error;
256 fn from_str(s: &str) -> Result<$elem, Error> {
257 // TODO: add a way to parse that differently when needed.
258 Ok($elem(String::from(s)))
259 }
260 }
261 impl IntoAttributeValue for $elem {
262 fn into_attribute_value(self) -> Option<String> {
263 Some(self.0)
264 }
265 }
266 );
267}
268
269macro_rules! generate_elem_id {
270 ($elem:ident, $name:tt, $ns:expr) => (
271 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
272 pub struct $elem(pub String);
273 impl FromStr for $elem {
274 type Err = Error;
275 fn from_str(s: &str) -> Result<$elem, Error> {
276 // TODO: add a way to parse that differently when needed.
277 Ok($elem(String::from(s)))
278 }
279 }
280 impl From<$elem> for Element {
281 fn from(elem: $elem) -> Element {
282 Element::builder($name)
283 .ns($ns)
284 .append(elem.0)
285 .build()
286 }
287 }
288 );
289}
290
291macro_rules! generate_element_with_text {
292 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+], $text_ident:ident: $codec:ident < $text_type:ty >) => (
293 $(#[$meta])*
294 #[derive(Debug, Clone)]
295 pub struct $elem {
296 $(
297 $(#[$attr_meta])*
298 pub $attr: $attr_type,
299 )*
300 pub $text_ident: $text_type,
301 }
302
303 impl TryFrom<Element> for $elem {
304 type Err = Error;
305
306 fn try_from(elem: Element) -> Result<$elem, Error> {
307 check_self!(elem, $name, $ns);
308 check_no_children!(elem, $name);
309 check_no_unknown_attributes!(elem, $name, [$($attr_name),*]);
310 Ok($elem {
311 $(
312 $attr: get_attr!(elem, $attr_name, $attr_action),
313 )*
314 $text_ident: $codec::decode(&elem.text())?,
315 })
316 }
317 }
318
319 impl From<$elem> for Element {
320 fn from(elem: $elem) -> Element {
321 Element::builder($name)
322 .ns($ns)
323 $(
324 .attr($attr_name, elem.$attr)
325 )*
326 .append($codec::encode(&elem.$text_ident))
327 .build()
328 }
329 }
330 );
331}
332
333macro_rules! generate_element_with_children {
334 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:expr) => $child_constructor:ident),+]) => (
335 $(#[$meta])*
336 #[derive(Debug, Clone)]
337 pub struct $elem {
338 $(
339 $(#[$attr_meta])*
340 pub $attr: $attr_type,
341 )*
342 $(
343 $(#[$child_meta])*
344 pub $child_ident: Vec<$child_type>,
345 )*
346 }
347
348 impl TryFrom<Element> for $elem {
349 type Err = Error;
350
351 fn try_from(elem: Element) -> Result<$elem, Error> {
352 check_self!(elem, $name, $ns);
353 check_no_unknown_attributes!(elem, $name, [$($attr_name),*]);
354 let mut parsed_children = vec!();
355 for child in elem.children() {
356 $(
357 if child.is($child_name, $child_ns) {
358 let parsed_child = $child_constructor::try_from(child.clone())?;
359 parsed_children.push(parsed_child);
360 continue;
361 }
362 )*
363 return Err(Error::ParseError(concat!("Unknown child in ", $name, " element.")));
364 }
365 Ok($elem {
366 $(
367 $attr: get_attr!(elem, $attr_name, $attr_action),
368 )*
369 $(
370 $child_ident: parsed_children,
371 )*
372 })
373 }
374 }
375
376 impl From<$elem> for Element {
377 fn from(elem: $elem) -> Element {
378 Element::builder($name)
379 .ns($ns)
380 $(
381 .attr($attr_name, elem.$attr)
382 )*
383 $(
384 .append(elem.$child_ident)
385 )*
386 .build()
387 }
388 }
389 );
390}