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