1// Copyright (c) 2017-2018 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::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 => ::std::default::Default::default(),
34 }
35 );
36}
37
38macro_rules! generate_attribute {
39 ($(#[$meta:meta])* $elem:ident, $name:tt, {$($(#[$a_meta:meta])* $a:ident => $b:tt),+,}) => (
40 generate_attribute!($(#[$meta])* $elem, $name, {$($(#[$a_meta])* $a => $b),+});
41 );
42 ($(#[$meta:meta])* $elem:ident, $name:tt, {$($(#[$a_meta:meta])* $a:ident => $b:tt),+,}, Default = $default:ident) => (
43 generate_attribute!($(#[$meta])* $elem, $name, {$($(#[$a_meta])* $a => $b),+}, Default = $default);
44 );
45 ($(#[$meta:meta])* $elem:ident, $name:tt, {$($(#[$a_meta:meta])* $a:ident => $b:tt),+}) => (
46 $(#[$meta])*
47 #[derive(Debug, Clone, PartialEq)]
48 pub enum $elem {
49 $(
50 $(#[$a_meta])*
51 $a
52 ),+
53 }
54 impl ::std::str::FromStr for $elem {
55 type Err = ::error::Error;
56 fn from_str(s: &str) -> Result<$elem, ::error::Error> {
57 Ok(match s {
58 $($b => $elem::$a),+,
59 _ => return Err(::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
60 })
61 }
62 }
63 impl ::minidom::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 ($(#[$meta:meta])* $elem:ident, $name:tt, {$($(#[$a_meta:meta])* $a:ident => $b:tt),+}, Default = $default:ident) => (
72 $(#[$meta])*
73 #[derive(Debug, Clone, PartialEq)]
74 pub enum $elem {
75 $(
76 $(#[$a_meta])*
77 $a
78 ),+
79 }
80 impl ::std::str::FromStr for $elem {
81 type Err = ::error::Error;
82 fn from_str(s: &str) -> Result<$elem, ::error::Error> {
83 Ok(match s {
84 $($b => $elem::$a),+,
85 _ => return Err(::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
86 })
87 }
88 }
89 impl ::minidom::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 ::std::default::Default for $elem {
99 fn default() -> $elem {
100 $elem::$default
101 }
102 }
103 );
104 ($(#[$meta:meta])* $elem:ident, $name:tt, bool) => (
105 $(#[$meta])*
106 #[derive(Debug, Clone, PartialEq)]
107 pub enum $elem {
108 /// True value, represented by either 'true' or '1'.
109 True,
110 /// False value, represented by either 'false' or '0'.
111 False,
112 }
113 impl ::std::str::FromStr for $elem {
114 type Err = ::error::Error;
115 fn from_str(s: &str) -> Result<Self, ::error::Error> {
116 Ok(match s {
117 "true" | "1" => $elem::True,
118 "false" | "0" => $elem::False,
119 _ => return Err(::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
120 })
121 }
122 }
123 impl ::minidom::IntoAttributeValue for $elem {
124 fn into_attribute_value(self) -> Option<String> {
125 match self {
126 $elem::True => Some(String::from("true")),
127 $elem::False => None
128 }
129 }
130 }
131 impl ::std::default::Default for $elem {
132 fn default() -> $elem {
133 $elem::False
134 }
135 }
136 );
137}
138
139macro_rules! generate_element_enum {
140 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+,}) => (
141 generate_element_enum!($(#[$meta])* $elem, $name, $ns, {$($(#[$enum_meta])* $enum => $enum_name),+});
142 );
143 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+}) => (
144 $(#[$meta])*
145 #[derive(Debug, Clone, PartialEq)]
146 pub enum $elem {
147 $(
148 $(#[$enum_meta])*
149 $enum
150 ),+
151 }
152 impl ::try_from::TryFrom<::minidom::Element> for $elem {
153 type Err = ::error::Error;
154 fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> {
155 check_ns_only!(elem, $name, $ns);
156 check_no_children!(elem, $name);
157 check_no_attributes!(elem, $name);
158 Ok(match elem.name() {
159 $($enum_name => $elem::$enum,)+
160 _ => return Err(::error::Error::ParseError(concat!("This is not a ", $name, " element."))),
161 })
162 }
163 }
164 impl From<$elem> for ::minidom::Element {
165 fn from(elem: $elem) -> ::minidom::Element {
166 ::minidom::Element::builder(match elem {
167 $($elem::$enum => $enum_name,)+
168 }).ns(::ns::$ns)
169 .build()
170 }
171 }
172 );
173}
174
175macro_rules! generate_attribute_enum {
176 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, $attr:tt, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+,}) => (
177 generate_attribute_enum!($(#[$meta])* $elem, $name, $ns, $attr, {$($(#[$enum_meta])* $enum => $enum_name),+});
178 );
179 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, $attr:tt, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+}) => (
180 $(#[$meta])*
181 #[derive(Debug, Clone, PartialEq)]
182 pub enum $elem {
183 $(
184 $(#[$enum_meta])*
185 $enum
186 ),+
187 }
188 impl ::try_from::TryFrom<::minidom::Element> for $elem {
189 type Err = ::error::Error;
190 fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> {
191 check_ns_only!(elem, $name, $ns);
192 check_no_children!(elem, $name);
193 check_no_unknown_attributes!(elem, $name, [$attr]);
194 Ok(match get_attr!(elem, $attr, required) {
195 $($enum_name => $elem::$enum,)+
196 _ => return Err(::error::Error::ParseError(concat!("Invalid ", $name, " ", $attr, " value."))),
197 })
198 }
199 }
200 impl From<$elem> for ::minidom::Element {
201 fn from(elem: $elem) -> ::minidom::Element {
202 ::minidom::Element::builder($name)
203 .ns(::ns::$ns)
204 .attr($attr, match elem {
205 $($elem::$enum => $enum_name,)+
206 })
207 .build()
208 }
209 }
210 );
211}
212
213macro_rules! check_self {
214 ($elem:ident, $name:tt, $ns:ident) => (
215 check_self!($elem, $name, $ns, $name);
216 );
217 ($elem:ident, $name:tt, $ns:ident, $pretty_name:tt) => (
218 if !$elem.is($name, ::ns::$ns) {
219 return Err(::error::Error::ParseError(concat!("This is not a ", $pretty_name, " element.")));
220 }
221 );
222}
223
224macro_rules! check_ns_only {
225 ($elem:ident, $name:tt, $ns:ident) => (
226 if !$elem.has_ns(::ns::$ns) {
227 return Err(::error::Error::ParseError(concat!("This is not a ", $name, " element.")));
228 }
229 );
230}
231
232macro_rules! check_no_children {
233 ($elem:ident, $name:tt) => (
234 for _ in $elem.children() {
235 return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element.")));
236 }
237 );
238}
239
240macro_rules! check_no_attributes {
241 ($elem:ident, $name:tt) => (
242 for _ in $elem.attrs() {
243 return Err(::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element.")));
244 }
245 );
246}
247
248macro_rules! check_no_unknown_attributes {
249 ($elem:ident, $name:tt, [$($attr:tt),*]) => (
250 for (_attr, _) in $elem.attrs() {
251 $(
252 if _attr == $attr {
253 continue;
254 }
255 )*
256 return Err(::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element.")));
257 }
258 );
259}
260
261macro_rules! generate_empty_element {
262 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident) => (
263 $(#[$meta])*
264 #[derive(Debug, Clone)]
265 pub struct $elem;
266
267 impl ::try_from::TryFrom<::minidom::Element> for $elem {
268 type Err = ::error::Error;
269
270 fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> {
271 check_self!(elem, $name, $ns);
272 check_no_children!(elem, $name);
273 check_no_attributes!(elem, $name);
274 Ok($elem)
275 }
276 }
277
278 impl From<$elem> for ::minidom::Element {
279 fn from(_: $elem) -> ::minidom::Element {
280 ::minidom::Element::builder($name)
281 .ns(::ns::$ns)
282 .build()
283 }
284 }
285 );
286}
287
288macro_rules! generate_id {
289 ($(#[$meta:meta])* $elem:ident) => (
290 $(#[$meta])*
291 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
292 pub struct $elem(pub String);
293 impl ::std::str::FromStr for $elem {
294 type Err = ::error::Error;
295 fn from_str(s: &str) -> Result<$elem, ::error::Error> {
296 // TODO: add a way to parse that differently when needed.
297 Ok($elem(String::from(s)))
298 }
299 }
300 impl ::minidom::IntoAttributeValue for $elem {
301 fn into_attribute_value(self) -> Option<String> {
302 Some(self.0)
303 }
304 }
305 );
306}
307
308macro_rules! generate_elem_id {
309 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident) => (
310 $(#[$meta])*
311 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
312 pub struct $elem(pub String);
313 impl ::std::str::FromStr for $elem {
314 type Err = ::error::Error;
315 fn from_str(s: &str) -> Result<$elem, ::error::Error> {
316 // TODO: add a way to parse that differently when needed.
317 Ok($elem(String::from(s)))
318 }
319 }
320 impl ::try_from::TryFrom<::minidom::Element> for $elem {
321 type Err = ::error::Error;
322 fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> {
323 check_self!(elem, $name, $ns);
324 check_no_children!(elem, $name);
325 check_no_attributes!(elem, $name);
326 // TODO: add a way to parse that differently when needed.
327 Ok($elem(elem.text()))
328 }
329 }
330 impl From<$elem> for ::minidom::Element {
331 fn from(elem: $elem) -> ::minidom::Element {
332 ::minidom::Element::builder($name)
333 .ns(::ns::$ns)
334 .append(elem.0)
335 .build()
336 }
337 }
338 );
339}
340
341macro_rules! start_decl {
342 (Vec, $type:ty) => (
343 Vec<$type>
344 );
345 (Option, $type:ty) => (
346 Option<$type>
347 );
348 (Required, $type:ty) => (
349 $type
350 );
351}
352
353macro_rules! start_parse_elem {
354 ($temp:ident: Vec) => (
355 let mut $temp = Vec::new();
356 );
357 ($temp:ident: Option) => (
358 let mut $temp = None;
359 );
360 ($temp:ident: Required) => (
361 let mut $temp = None;
362 );
363}
364
365macro_rules! do_parse {
366 ($elem:ident, Element) => (
367 $elem.clone()
368 );
369 ($elem:ident, String) => (
370 $elem.text()
371 );
372 ($elem:ident, $constructor:ident) => (
373 $constructor::try_from($elem.clone())?
374 );
375}
376
377macro_rules! do_parse_elem {
378 ($temp:ident: Vec = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => (
379 $temp.push(do_parse!($elem, $constructor));
380 );
381 ($temp:ident: Option = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => (
382 if $temp.is_some() {
383 return Err(::error::Error::ParseError(concat!("Element ", $parent_name, " must not have more than one ", $name, " child.")));
384 }
385 $temp = Some(do_parse!($elem, $constructor));
386 );
387 ($temp:ident: Required = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => (
388 if $temp.is_some() {
389 return Err(::error::Error::ParseError(concat!("Element ", $parent_name, " must not have more than one ", $name, " child.")));
390 }
391 $temp = Some(do_parse!($elem, $constructor));
392 );
393}
394
395macro_rules! finish_parse_elem {
396 ($temp:ident: Vec = $name:tt, $parent_name:tt) => (
397 $temp
398 );
399 ($temp:ident: Option = $name:tt, $parent_name:tt) => (
400 $temp
401 );
402 ($temp:ident: Required = $name:tt, $parent_name:tt) => (
403 $temp.ok_or(::error::Error::ParseError(concat!("Missing child ", $name, " in ", $parent_name, " element.")))?
404 );
405}
406
407macro_rules! generate_serialiser {
408 ($parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => (
409 ::minidom::Element::builder($name)
410 .ns(::ns::$ns)
411 .append($parent.$elem)
412 .build()
413 );
414 ($parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => (
415 $parent.$elem.map(|elem|
416 ::minidom::Element::builder($name)
417 .ns(::ns::$ns)
418 .append(elem)
419 .build())
420 );
421 ($parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => (
422 $parent.$elem
423 );
424}
425
426macro_rules! generate_element {
427 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => (
428 generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []);
429 );
430 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => (
431 generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []);
432 );
433 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => (
434 generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]);
435 );
436 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => (
437 generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]);
438 );
439 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >)) => (
440 generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [], text: ($(#[$text_meta])* $text_ident: $codec<$text_type>));
441 );
442 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+], text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >)) => (
443 generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [], text: ($(#[$text_meta])* $text_ident: $codec<$text_type>));
444 );
445 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*] $(, text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >))*) => (
446 $(#[$meta])*
447 #[derive(Debug, Clone)]
448 pub struct $elem {
449 $(
450 $(#[$attr_meta])*
451 pub $attr: $attr_type,
452 )*
453 $(
454 $(#[$child_meta])*
455 pub $child_ident: start_decl!($coucou, $child_type),
456 )*
457 $(
458 $(#[$text_meta])*
459 pub $text_ident: $text_type,
460 )*
461 }
462
463 impl ::try_from::TryFrom<::minidom::Element> for $elem {
464 type Err = ::error::Error;
465
466 fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> {
467 check_self!(elem, $name, $ns);
468 check_no_unknown_attributes!(elem, $name, [$($attr_name),*]);
469 $(
470 start_parse_elem!($child_ident: $coucou);
471 )*
472 for _child in elem.children() {
473 $(
474 if _child.is($child_name, ::ns::$child_ns) {
475 do_parse_elem!($child_ident: $coucou = $child_constructor => _child, $child_name, $name);
476 continue;
477 }
478 )*
479 return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element.")));
480 }
481 Ok($elem {
482 $(
483 $attr: get_attr!(elem, $attr_name, $attr_action),
484 )*
485 $(
486 $child_ident: finish_parse_elem!($child_ident: $coucou = $child_name, $name),
487 )*
488 $(
489 $text_ident: $codec::decode(&elem.text())?,
490 )*
491 })
492 }
493 }
494
495 impl From<$elem> for ::minidom::Element {
496 fn from(elem: $elem) -> ::minidom::Element {
497 ::minidom::Element::builder($name)
498 .ns(::ns::$ns)
499 $(
500 .attr($attr_name, elem.$attr)
501 )*
502 $(
503 .append(generate_serialiser!(elem, $child_ident, $coucou, $child_constructor, ($child_name, $child_ns)))
504 )*
505 $(
506 .append($codec::encode(&elem.$text_ident))
507 )*
508 .build()
509 }
510 }
511 );
512}