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