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, OptionEmpty, $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, Option, $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 => {
28 return Err(crate::util::error::Error::ParseError(concat!(
29 "Required attribute '",
30 $attr,
31 "' missing."
32 )));
33 }
34 }
35 };
36 ($elem:ident, $attr:tt, RequiredNonEmpty, $value:ident, $func:expr) => {
37 match $elem.attr($attr) {
38 Some("") => {
39 return Err(crate::util::error::Error::ParseError(concat!(
40 "Required attribute '",
41 $attr,
42 "' must not be empty."
43 )));
44 }
45 Some($value) => $func,
46 None => {
47 return Err(crate::util::error::Error::ParseError(concat!(
48 "Required attribute '",
49 $attr,
50 "' missing."
51 )));
52 }
53 }
54 };
55 ($elem:ident, $attr:tt, Default, $value:ident, $func:expr) => {
56 match $elem.attr($attr) {
57 Some($value) => $func,
58 None => ::std::default::Default::default(),
59 }
60 };
61}
62
63macro_rules! generate_attribute {
64 ($(#[$meta:meta])* $elem:ident, $name:tt, {$($(#[$a_meta:meta])* $a:ident => $b:tt),+,}) => (
65 generate_attribute!($(#[$meta])* $elem, $name, {$($(#[$a_meta])* $a => $b),+});
66 );
67 ($(#[$meta:meta])* $elem:ident, $name:tt, {$($(#[$a_meta:meta])* $a:ident => $b:tt),+,}, Default = $default:ident) => (
68 generate_attribute!($(#[$meta])* $elem, $name, {$($(#[$a_meta])* $a => $b),+}, Default = $default);
69 );
70 ($(#[$meta:meta])* $elem:ident, $name:tt, {$($(#[$a_meta:meta])* $a:ident => $b:tt),+}) => (
71 $(#[$meta])*
72 #[derive(Debug, Clone, PartialEq)]
73 pub enum $elem {
74 $(
75 $(#[$a_meta])*
76 $a
77 ),+
78 }
79 impl ::std::str::FromStr for $elem {
80 type Err = crate::util::error::Error;
81 fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> {
82 Ok(match s {
83 $($b => $elem::$a),+,
84 _ => return Err(crate::util::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
85 })
86 }
87 }
88 impl std::fmt::Display for $elem {
89 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
90 write!(fmt, "{}", match self {
91 $($elem::$a => $b),+
92 })
93 }
94 }
95 impl ::minidom::IntoAttributeValue for $elem {
96 fn into_attribute_value(self) -> Option<String> {
97 Some(String::from(match self {
98 $($elem::$a => $b),+
99 }))
100 }
101 }
102 );
103 ($(#[$meta:meta])* $elem:ident, $name:tt, {$($(#[$a_meta:meta])* $a:ident => $b:tt),+}, Default = $default:ident) => (
104 $(#[$meta])*
105 #[derive(Debug, Clone, PartialEq)]
106 pub enum $elem {
107 $(
108 $(#[$a_meta])*
109 $a
110 ),+
111 }
112 impl ::std::str::FromStr for $elem {
113 type Err = crate::util::error::Error;
114 fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> {
115 Ok(match s {
116 $($b => $elem::$a),+,
117 _ => return Err(crate::util::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
118 })
119 }
120 }
121 impl ::minidom::IntoAttributeValue for $elem {
122 #[allow(unreachable_patterns)]
123 fn into_attribute_value(self) -> Option<String> {
124 Some(String::from(match self {
125 $elem::$default => return None,
126 $($elem::$a => $b),+
127 }))
128 }
129 }
130 impl ::std::default::Default for $elem {
131 fn default() -> $elem {
132 $elem::$default
133 }
134 }
135 );
136 ($(#[$meta:meta])* $elem:ident, $name:tt, ($(#[$meta_symbol:meta])* $symbol:ident => $value:tt)) => (
137 $(#[$meta])*
138 #[derive(Debug, Clone, PartialEq)]
139 pub enum $elem {
140 $(#[$meta_symbol])*
141 $symbol,
142 /// Value when absent.
143 None,
144 }
145 impl ::std::str::FromStr for $elem {
146 type Err = crate::util::error::Error;
147 fn from_str(s: &str) -> Result<Self, crate::util::error::Error> {
148 Ok(match s {
149 $value => $elem::$symbol,
150 _ => return Err(crate::util::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
151 })
152 }
153 }
154 impl ::minidom::IntoAttributeValue for $elem {
155 fn into_attribute_value(self) -> Option<String> {
156 match self {
157 $elem::$symbol => Some(String::from($value)),
158 $elem::None => None
159 }
160 }
161 }
162 impl ::std::default::Default for $elem {
163 fn default() -> $elem {
164 $elem::None
165 }
166 }
167 );
168 ($(#[$meta:meta])* $elem:ident, $name:tt, bool) => (
169 $(#[$meta])*
170 #[derive(Debug, Clone, PartialEq)]
171 pub enum $elem {
172 /// True value, represented by either 'true' or '1'.
173 True,
174 /// False value, represented by either 'false' or '0'.
175 False,
176 }
177 impl ::std::str::FromStr for $elem {
178 type Err = crate::util::error::Error;
179 fn from_str(s: &str) -> Result<Self, crate::util::error::Error> {
180 Ok(match s {
181 "true" | "1" => $elem::True,
182 "false" | "0" => $elem::False,
183 _ => return Err(crate::util::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
184 })
185 }
186 }
187 impl ::minidom::IntoAttributeValue for $elem {
188 fn into_attribute_value(self) -> Option<String> {
189 match self {
190 $elem::True => Some(String::from("true")),
191 $elem::False => None
192 }
193 }
194 }
195 impl ::std::default::Default for $elem {
196 fn default() -> $elem {
197 $elem::False
198 }
199 }
200 );
201 ($(#[$meta:meta])* $elem:ident, $name:tt, $type:tt, Default = $default:expr) => (
202 $(#[$meta])*
203 #[derive(Debug, Clone, PartialEq)]
204 pub struct $elem(pub $type);
205 impl ::std::str::FromStr for $elem {
206 type Err = crate::util::error::Error;
207 fn from_str(s: &str) -> Result<Self, crate::util::error::Error> {
208 Ok($elem($type::from_str(s)?))
209 }
210 }
211 impl ::minidom::IntoAttributeValue for $elem {
212 fn into_attribute_value(self) -> Option<String> {
213 match self {
214 $elem($default) => None,
215 $elem(value) => Some(format!("{}", value)),
216 }
217 }
218 }
219 impl ::std::default::Default for $elem {
220 fn default() -> $elem {
221 $elem($default)
222 }
223 }
224 );
225}
226
227macro_rules! generate_element_enum {
228 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+,}) => (
229 generate_element_enum!($(#[$meta])* $elem, $name, $ns, {$($(#[$enum_meta])* $enum => $enum_name),+});
230 );
231 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+}) => (
232 $(#[$meta])*
233 #[derive(Debug, Clone, PartialEq)]
234 pub enum $elem {
235 $(
236 $(#[$enum_meta])*
237 $enum
238 ),+
239 }
240 impl ::std::convert::TryFrom<crate::Element> for $elem {
241 type Error = crate::util::error::Error;
242 fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> {
243 check_ns_only!(elem, $name, $ns);
244 check_no_children!(elem, $name);
245 check_no_attributes!(elem, $name);
246 Ok(match elem.name() {
247 $($enum_name => $elem::$enum,)+
248 _ => return Err(crate::util::error::Error::ParseError(concat!("This is not a ", $name, " element."))),
249 })
250 }
251 }
252 impl From<$elem> for crate::Element {
253 fn from(elem: $elem) -> crate::Element {
254 crate::Element::builder(
255 match elem {
256 $($elem::$enum => $enum_name,)+
257 },
258 crate::ns::$ns,
259 )
260 .build()
261 }
262 }
263 );
264}
265
266macro_rules! generate_attribute_enum {
267 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, $attr:tt, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+,}) => (
268 generate_attribute_enum!($(#[$meta])* $elem, $name, $ns, $attr, {$($(#[$enum_meta])* $enum => $enum_name),+});
269 );
270 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, $attr:tt, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+}) => (
271 $(#[$meta])*
272 #[derive(Debug, Clone, PartialEq)]
273 pub enum $elem {
274 $(
275 $(#[$enum_meta])*
276 $enum
277 ),+
278 }
279 impl ::std::convert::TryFrom<crate::Element> for $elem {
280 type Error = crate::util::error::Error;
281 fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> {
282 check_ns_only!(elem, $name, $ns);
283 check_no_children!(elem, $name);
284 check_no_unknown_attributes!(elem, $name, [$attr]);
285 Ok(match get_attr!(elem, $attr, Required) {
286 $($enum_name => $elem::$enum,)+
287 _ => return Err(crate::util::error::Error::ParseError(concat!("Invalid ", $name, " ", $attr, " value."))),
288 })
289 }
290 }
291 impl From<$elem> for crate::Element {
292 fn from(elem: $elem) -> crate::Element {
293 crate::Element::builder($name, crate::ns::$ns)
294 .attr($attr, match elem {
295 $($elem::$enum => $enum_name,)+
296 })
297 .build()
298 }
299 }
300 );
301}
302
303macro_rules! check_self {
304 ($elem:ident, $name:tt, $ns:ident) => {
305 check_self!($elem, $name, $ns, $name);
306 };
307 ($elem:ident, $name:tt, $ns:ident, $pretty_name:tt) => {
308 if !$elem.is($name, crate::ns::$ns) {
309 return Err(crate::util::error::Error::ParseError(concat!(
310 "This is not a ",
311 $pretty_name,
312 " element."
313 )));
314 }
315 };
316}
317
318macro_rules! check_ns_only {
319 ($elem:ident, $name:tt, $ns:ident) => {
320 if !$elem.has_ns(crate::ns::$ns) {
321 return Err(crate::util::error::Error::ParseError(concat!(
322 "This is not a ",
323 $name,
324 " element."
325 )));
326 }
327 };
328}
329
330macro_rules! check_no_children {
331 ($elem:ident, $name:tt) => {
332 #[cfg(not(feature = "disable-validation"))]
333 for _ in $elem.children() {
334 return Err(crate::util::error::Error::ParseError(concat!(
335 "Unknown child in ",
336 $name,
337 " element."
338 )));
339 }
340 };
341}
342
343macro_rules! check_no_attributes {
344 ($elem:ident, $name:tt) => {
345 #[cfg(not(feature = "disable-validation"))]
346 for _ in $elem.attrs() {
347 return Err(crate::util::error::Error::ParseError(concat!(
348 "Unknown attribute in ",
349 $name,
350 " element."
351 )));
352 }
353 };
354}
355
356macro_rules! check_no_unknown_attributes {
357 ($elem:ident, $name:tt, [$($attr:tt),*]) => (
358 #[cfg(not(feature = "disable-validation"))]
359 for (_attr, _) in $elem.attrs() {
360 $(
361 if _attr == $attr {
362 continue;
363 }
364 )*
365 return Err(crate::util::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element.")));
366 }
367 );
368}
369
370macro_rules! generate_empty_element {
371 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident) => (
372 $(#[$meta])*
373 #[derive(Debug, Clone, PartialEq)]
374 pub struct $elem;
375
376 impl ::std::convert::TryFrom<crate::Element> for $elem {
377 type Error = crate::util::error::Error;
378
379 fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> {
380 check_self!(elem, $name, $ns);
381 check_no_children!(elem, $name);
382 check_no_attributes!(elem, $name);
383 Ok($elem)
384 }
385 }
386
387 impl From<$elem> for crate::Element {
388 fn from(_: $elem) -> crate::Element {
389 crate::Element::builder($name, crate::ns::$ns)
390 .build()
391 }
392 }
393 );
394}
395
396macro_rules! generate_id {
397 ($(#[$meta:meta])* $elem:ident) => (
398 $(#[$meta])*
399 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
400 pub struct $elem(pub String);
401 impl ::std::str::FromStr for $elem {
402 type Err = crate::util::error::Error;
403 fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> {
404 // TODO: add a way to parse that differently when needed.
405 Ok($elem(String::from(s)))
406 }
407 }
408 impl ::minidom::IntoAttributeValue for $elem {
409 fn into_attribute_value(self) -> Option<String> {
410 Some(self.0)
411 }
412 }
413 );
414}
415
416macro_rules! generate_elem_id {
417 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident) => (
418 generate_elem_id!($(#[$meta])* $elem, $name, $ns, String);
419 impl ::std::str::FromStr for $elem {
420 type Err = crate::util::error::Error;
421 fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> {
422 // TODO: add a way to parse that differently when needed.
423 Ok($elem(String::from(s)))
424 }
425 }
426 );
427 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, $type:ty) => (
428 $(#[$meta])*
429 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
430 pub struct $elem(pub $type);
431 impl ::std::convert::TryFrom<crate::Element> for $elem {
432 type Error = crate::util::error::Error;
433 fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> {
434 check_self!(elem, $name, $ns);
435 check_no_children!(elem, $name);
436 check_no_attributes!(elem, $name);
437 // TODO: add a way to parse that differently when needed.
438 Ok($elem(elem.text().parse()?))
439 }
440 }
441 impl From<$elem> for crate::Element {
442 fn from(elem: $elem) -> crate::Element {
443 crate::Element::builder($name, crate::ns::$ns)
444 .append(elem.0.to_string())
445 .build()
446 }
447 }
448 );
449}
450
451macro_rules! decl_attr {
452 (OptionEmpty, $type:ty) => (
453 Option<$type>
454 );
455 (Option, $type:ty) => (
456 Option<$type>
457 );
458 (Required, $type:ty) => (
459 $type
460 );
461 (RequiredNonEmpty, $type:ty) => (
462 $type
463 );
464 (Default, $type:ty) => (
465 $type
466 );
467}
468
469macro_rules! start_decl {
470 (Vec, $type:ty) => (
471 Vec<$type>
472 );
473 (Option, $type:ty) => (
474 Option<$type>
475 );
476 (Required, $type:ty) => (
477 $type
478 );
479 (Present, $type:ty) => (
480 bool
481 );
482}
483
484macro_rules! start_parse_elem {
485 ($temp:ident: Vec) => {
486 let mut $temp = Vec::new();
487 };
488 ($temp:ident: Option) => {
489 let mut $temp = None;
490 };
491 ($temp:ident: Required) => {
492 let mut $temp = None;
493 };
494 ($temp:ident: Present) => {
495 let mut $temp = false;
496 };
497}
498
499macro_rules! do_parse {
500 ($elem:ident, Element) => {
501 $elem.clone()
502 };
503 ($elem:ident, String) => {
504 $elem.text()
505 };
506 ($elem:ident, $constructor:ident) => {
507 $constructor::try_from($elem.clone())?
508 };
509}
510
511macro_rules! do_parse_elem {
512 ($temp:ident: Vec = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => {
513 $temp.push(do_parse!($elem, $constructor));
514 };
515 ($temp:ident: Option = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => {
516 if $temp.is_some() {
517 return Err(crate::util::error::Error::ParseError(concat!(
518 "Element ",
519 $parent_name,
520 " must not have more than one ",
521 $name,
522 " child."
523 )));
524 }
525 $temp = Some(do_parse!($elem, $constructor));
526 };
527 ($temp:ident: Required = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => {
528 if $temp.is_some() {
529 return Err(crate::util::error::Error::ParseError(concat!(
530 "Element ",
531 $parent_name,
532 " must not have more than one ",
533 $name,
534 " child."
535 )));
536 }
537 $temp = Some(do_parse!($elem, $constructor));
538 };
539 ($temp:ident: Present = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => {
540 if $temp {
541 return Err(crate::util::error::Error::ParseError(concat!(
542 "Element ",
543 $parent_name,
544 " must not have more than one ",
545 $name,
546 " child."
547 )));
548 }
549 $temp = true;
550 };
551}
552
553macro_rules! finish_parse_elem {
554 ($temp:ident: Vec = $name:tt, $parent_name:tt) => {
555 $temp
556 };
557 ($temp:ident: Option = $name:tt, $parent_name:tt) => {
558 $temp
559 };
560 ($temp:ident: Required = $name:tt, $parent_name:tt) => {
561 $temp.ok_or(crate::util::error::Error::ParseError(concat!(
562 "Missing child ",
563 $name,
564 " in ",
565 $parent_name,
566 " element."
567 )))?
568 };
569 ($temp:ident: Present = $name:tt, $parent_name:tt) => {
570 $temp
571 };
572}
573
574macro_rules! generate_serialiser {
575 ($builder:ident, $parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => {
576 $builder.append(
577 crate::Element::builder($name, crate::ns::$ns)
578 .append(::minidom::Node::Text($parent.$elem)),
579 )
580 };
581 ($builder:ident, $parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => {
582 $builder.append_all($parent.$elem.map(|elem| {
583 crate::Element::builder($name, crate::ns::$ns).append(::minidom::Node::Text(elem))
584 }))
585 };
586 ($builder:ident, $parent:ident, $elem:ident, Option, $constructor:ident, ($name:tt, *)) => {
587 $builder.append_all(
588 $parent
589 .$elem
590 .map(|elem| ::minidom::Node::Element(crate::Element::from(elem))),
591 )
592 };
593 ($builder:ident, $parent:ident, $elem:ident, Option, $constructor:ident, ($name:tt, $ns:ident)) => {
594 $builder.append_all(
595 $parent
596 .$elem
597 .map(|elem| ::minidom::Node::Element(crate::Element::from(elem))),
598 )
599 };
600 ($builder:ident, $parent:ident, $elem:ident, Vec, $constructor:ident, ($name:tt, $ns:ident)) => {
601 $builder.append_all($parent.$elem.into_iter())
602 };
603 ($builder:ident, $parent:ident, $elem:ident, Present, $constructor:ident, ($name:tt, $ns:ident)) => {
604 $builder.append(::minidom::Node::Element(
605 crate::Element::builder($name, crate::ns::$ns).build(),
606 ))
607 };
608 ($builder:ident, $parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => {
609 $builder.append(::minidom::Node::Element(crate::Element::from(
610 $parent.$elem,
611 )))
612 };
613}
614
615macro_rules! generate_child_test {
616 ($child:ident, $name:tt, *) => {
617 $child.is($name, ::minidom::NSChoice::Any)
618 };
619 ($child:ident, $name:tt, $ns:tt) => {
620 $child.is($name, crate::ns::$ns)
621 };
622}
623
624macro_rules! generate_element {
625 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),+,]) => (
626 generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: []);
627 );
628 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),+]) => (
629 generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: []);
630 );
631 ($(#[$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:tt) => $child_constructor:ident),*]) => (
632 generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]);
633 );
634 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:tt) => $child_constructor:ident),*]) => (
635 generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]);
636 );
637 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >)) => (
638 generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [], text: ($(#[$text_meta])* $text_ident: $codec<$text_type>));
639 );
640 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),+], text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >)) => (
641 generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: [], text: ($(#[$text_meta])* $text_ident: $codec<$text_type>));
642 );
643 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:tt) => $child_constructor:ident),*] $(, text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >))*) => (
644 $(#[$meta])*
645 #[derive(Debug, Clone, PartialEq)]
646 pub struct $elem {
647 $(
648 $(#[$attr_meta])*
649 pub $attr: decl_attr!($attr_action, $attr_type),
650 )*
651 $(
652 $(#[$child_meta])*
653 pub $child_ident: start_decl!($coucou, $child_type),
654 )*
655 $(
656 $(#[$text_meta])*
657 pub $text_ident: $text_type,
658 )*
659 }
660
661 impl ::std::convert::TryFrom<crate::Element> for $elem {
662 type Error = crate::util::error::Error;
663
664 fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> {
665 check_self!(elem, $name, $ns);
666 check_no_unknown_attributes!(elem, $name, [$($attr_name),*]);
667 $(
668 start_parse_elem!($child_ident: $coucou);
669 )*
670 for _child in elem.children() {
671 $(
672 if generate_child_test!(_child, $child_name, $child_ns) {
673 do_parse_elem!($child_ident: $coucou = $child_constructor => _child, $child_name, $name);
674 continue;
675 }
676 )*
677 return Err(crate::util::error::Error::ParseError(concat!("Unknown child in ", $name, " element.")));
678 }
679 Ok($elem {
680 $(
681 $attr: get_attr!(elem, $attr_name, $attr_action),
682 )*
683 $(
684 $child_ident: finish_parse_elem!($child_ident: $coucou = $child_name, $name),
685 )*
686 $(
687 $text_ident: $codec::decode(&elem.text())?,
688 )*
689 })
690 }
691 }
692
693 impl From<$elem> for crate::Element {
694 fn from(elem: $elem) -> crate::Element {
695 let mut builder = crate::Element::builder($name, crate::ns::$ns);
696 $(
697 builder = builder.attr($attr_name, elem.$attr);
698 )*
699 $(
700 builder = generate_serialiser!(builder, elem, $child_ident, $coucou, $child_constructor, ($child_name, $child_ns));
701 )*
702 $(
703 builder = builder.append_all($codec::encode(&elem.$text_ident).map(::minidom::Node::Text).into_iter());
704 )*
705
706 builder.build()
707 }
708 }
709 );
710}
711
712#[cfg(test)]
713macro_rules! assert_size (
714 ($t:ty, $sz:expr) => (
715 assert_eq!(::std::mem::size_of::<$t>(), $sz);
716 );
717);
718
719// TODO: move that to src/pubsub/mod.rs, once we figure out how to use macros from there.
720macro_rules! impl_pubsub_item {
721 ($item:ident, $ns:ident) => {
722 impl ::std::convert::TryFrom<crate::Element> for $item {
723 type Error = Error;
724
725 fn try_from(elem: crate::Element) -> Result<$item, Error> {
726 check_self!(elem, "item", $ns);
727 check_no_unknown_attributes!(elem, "item", ["id", "publisher"]);
728 let mut payloads = elem.children().cloned().collect::<Vec<_>>();
729 let payload = payloads.pop();
730 if !payloads.is_empty() {
731 return Err(Error::ParseError(
732 "More than a single payload in item element.",
733 ));
734 }
735 Ok($item(crate::pubsub::Item {
736 id: get_attr!(elem, "id", Option),
737 publisher: get_attr!(elem, "publisher", Option),
738 payload,
739 }))
740 }
741 }
742
743 impl From<$item> for crate::Element {
744 fn from(item: $item) -> crate::Element {
745 crate::Element::builder("item", ns::$ns)
746 .attr("id", item.0.id)
747 .attr("publisher", item.0.publisher)
748 .append_all(item.0.payload)
749 .build()
750 }
751 }
752
753 impl ::std::ops::Deref for $item {
754 type Target = crate::pubsub::Item;
755
756 fn deref(&self) -> &Self::Target {
757 &self.0
758 }
759 }
760
761 impl ::std::ops::DerefMut for $item {
762 fn deref_mut(&mut self) -> &mut Self::Target {
763 &mut self.0
764 }
765 }
766 };
767}