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_element_with_only_attributes {
289 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => (
290 generate_element_with_only_attributes!($(#[$meta])* $elem, $name, $ns, [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*]);
291 );
292 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => (
293 $(#[$meta])*
294 #[derive(Debug, Clone)]
295 pub struct $elem {
296 $(
297 $(#[$attr_meta])*
298 pub $attr: $attr_type,
299 )*
300 }
301
302 impl ::try_from::TryFrom<::minidom::Element> for $elem {
303 type Err = ::error::Error;
304
305 fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> {
306 check_self!(elem, $name, $ns);
307 check_no_children!(elem, $name);
308 check_no_unknown_attributes!(elem, $name, [$($attr_name),*]);
309 Ok($elem {
310 $(
311 $attr: get_attr!(elem, $attr_name, $attr_action),
312 )*
313 })
314 }
315 }
316
317 impl From<$elem> for ::minidom::Element {
318 fn from(elem: $elem) -> ::minidom::Element {
319 ::minidom::Element::builder($name)
320 .ns(::ns::$ns)
321 $(
322 .attr($attr_name, elem.$attr)
323 )*
324 .build()
325 }
326 }
327 );
328}
329
330macro_rules! generate_id {
331 ($elem:ident) => (
332 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
333 pub struct $elem(pub String);
334 impl ::std::str::FromStr for $elem {
335 type Err = ::error::Error;
336 fn from_str(s: &str) -> Result<$elem, ::error::Error> {
337 // TODO: add a way to parse that differently when needed.
338 Ok($elem(String::from(s)))
339 }
340 }
341 impl ::minidom::IntoAttributeValue for $elem {
342 fn into_attribute_value(self) -> Option<String> {
343 Some(self.0)
344 }
345 }
346 );
347}
348
349macro_rules! generate_elem_id {
350 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident) => (
351 $(#[$meta])*
352 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
353 pub struct $elem(pub String);
354 impl ::std::str::FromStr for $elem {
355 type Err = ::error::Error;
356 fn from_str(s: &str) -> Result<$elem, ::error::Error> {
357 // TODO: add a way to parse that differently when needed.
358 Ok($elem(String::from(s)))
359 }
360 }
361 impl ::try_from::TryFrom<::minidom::Element> for $elem {
362 type Err = ::error::Error;
363 fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> {
364 check_self!(elem, $name, $ns);
365 check_no_children!(elem, $name);
366 check_no_attributes!(elem, $name);
367 // TODO: add a way to parse that differently when needed.
368 Ok($elem(elem.text()))
369 }
370 }
371 impl From<$elem> for ::minidom::Element {
372 fn from(elem: $elem) -> ::minidom::Element {
373 ::minidom::Element::builder($name)
374 .ns(::ns::$ns)
375 .append(elem.0)
376 .build()
377 }
378 }
379 );
380}
381
382macro_rules! generate_element_with_text {
383 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, $text_ident:ident: $codec:ident < $text_type:ty >) => (
384 generate_element_with_text!($(#[$meta])* $elem, $name, $ns, [], $text_ident: $codec<$text_type>);
385 );
386 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], $text_ident:ident: $codec:ident < $text_type:ty >) => (
387 $(#[$meta])*
388 #[derive(Debug, Clone)]
389 pub struct $elem {
390 $(
391 $(#[$attr_meta])*
392 pub $attr: $attr_type,
393 )*
394 pub $text_ident: $text_type,
395 }
396
397 impl ::try_from::TryFrom<::minidom::Element> for $elem {
398 type Err = ::error::Error;
399
400 fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> {
401 check_self!(elem, $name, $ns);
402 check_no_children!(elem, $name);
403 check_no_unknown_attributes!(elem, $name, [$($attr_name),*]);
404 Ok($elem {
405 $(
406 $attr: get_attr!(elem, $attr_name, $attr_action),
407 )*
408 $text_ident: $codec::decode(&elem.text())?,
409 })
410 }
411 }
412
413 impl From<$elem> for ::minidom::Element {
414 fn from(elem: $elem) -> ::minidom::Element {
415 ::minidom::Element::builder($name)
416 .ns(::ns::$ns)
417 $(
418 .attr($attr_name, elem.$attr)
419 )*
420 .append($codec::encode(&elem.$text_ident))
421 .build()
422 }
423 }
424 );
425}
426
427macro_rules! start_decl {
428 (Vec, $type:ty) => (
429 Vec<$type>
430 );
431 (Option, $type:ty) => (
432 Option<$type>
433 );
434 (Required, $type:ty) => (
435 $type
436 );
437}
438
439macro_rules! start_parse_elem {
440 ($temp:ident: Vec) => (
441 let mut $temp = Vec::new();
442 );
443 ($temp:ident: Option) => (
444 let mut $temp = None;
445 );
446 ($temp:ident: Required) => (
447 let mut $temp = None;
448 );
449}
450
451macro_rules! do_parse {
452 ($elem:ident, Element) => (
453 $elem.clone()
454 );
455 ($elem:ident, String) => (
456 $elem.text()
457 );
458 ($elem:ident, $constructor:ident) => (
459 $constructor::try_from($elem.clone())?
460 );
461}
462
463macro_rules! do_parse_elem {
464 ($temp:ident: Vec = $constructor:ident => $elem:ident) => (
465 $temp.push(do_parse!($elem, $constructor));
466 );
467 ($temp:ident: Option = $constructor:ident => $elem:ident) => (
468 if $temp.is_some() {
469 return Err(::error::Error::ParseError(concat!("coucou", " must not have more than one ", "coucou", ".")));
470 }
471 $temp = Some(do_parse!($elem, $constructor));
472 );
473 ($temp:ident: Required = $constructor:ident => $elem:ident) => (
474 $temp = Some(do_parse!($elem, $constructor));
475 );
476}
477
478macro_rules! finish_parse_elem {
479 ($temp:ident: Vec = $name:tt) => (
480 $temp
481 );
482 ($temp:ident: Option = $name:tt) => (
483 $temp
484 );
485 ($temp:ident: Required = $name:tt) => (
486 $temp.ok_or(::error::Error::ParseError(concat!("Missing child coucou in ", $name, " element.")))?
487 );
488}
489
490macro_rules! generate_serialiser {
491 ($parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => (
492 ::minidom::Element::builder($name)
493 .ns(::ns::$ns)
494 .append($parent.$elem)
495 .build()
496 );
497 ($parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => (
498 $parent.$elem.map(|elem|
499 ::minidom::Element::builder($name)
500 .ns(::ns::$ns)
501 .append(elem)
502 .build())
503 );
504 ($parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => (
505 $parent.$elem
506 );
507}
508
509macro_rules! generate_element_with_children {
510 ($(#[$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),+]) => (
511 generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),+]);
512 );
513 ($(#[$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),+]) => (
514 generate_element_with_children!($(#[$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),+]);
515 );
516 ($(#[$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),+]) => (
517 $(#[$meta])*
518 #[derive(Debug, Clone)]
519 pub struct $elem {
520 $(
521 $(#[$attr_meta])*
522 pub $attr: $attr_type,
523 )*
524 $(
525 $(#[$child_meta])*
526 pub $child_ident: start_decl!($coucou, $child_type),
527 )*
528 }
529
530 impl ::try_from::TryFrom<::minidom::Element> for $elem {
531 type Err = ::error::Error;
532
533 fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> {
534 check_self!(elem, $name, $ns);
535 check_no_unknown_attributes!(elem, $name, [$($attr_name),*]);
536 $(
537 start_parse_elem!($child_ident: $coucou);
538 )*
539 for child in elem.children() {
540 $(
541 if child.is($child_name, ::ns::$child_ns) {
542 do_parse_elem!($child_ident: $coucou = $child_constructor => child);
543 continue;
544 }
545 )*
546 return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element.")));
547 }
548 Ok($elem {
549 $(
550 $attr: get_attr!(elem, $attr_name, $attr_action),
551 )*
552 $(
553 $child_ident: finish_parse_elem!($child_ident: $coucou = $child_name),
554 )*
555 })
556 }
557 }
558
559 impl From<$elem> for ::minidom::Element {
560 fn from(elem: $elem) -> ::minidom::Element {
561 ::minidom::Element::builder($name)
562 .ns(::ns::$ns)
563 $(
564 .attr($attr_name, elem.$attr)
565 )*
566 $(
567 .append(generate_serialiser!(elem, $child_ident, $coucou, $child_constructor, ($child_name, $child_ns)))
568 )*
569 .build()
570 }
571 }
572 );
573}