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, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+], $text_ident:ident: $codec:ident < $text_type:ty >) => (
348 $(#[$meta])*
349 #[derive(Debug, Clone)]
350 pub struct $elem {
351 $(
352 $(#[$attr_meta])*
353 pub $attr: $attr_type,
354 )*
355 pub $text_ident: $text_type,
356 }
357
358 impl TryFrom<Element> for $elem {
359 type Err = Error;
360
361 fn try_from(elem: Element) -> Result<$elem, Error> {
362 check_self!(elem, $name, $ns);
363 check_no_children!(elem, $name);
364 check_no_unknown_attributes!(elem, $name, [$($attr_name),*]);
365 Ok($elem {
366 $(
367 $attr: get_attr!(elem, $attr_name, $attr_action),
368 )*
369 $text_ident: $codec::decode(&elem.text())?,
370 })
371 }
372 }
373
374 impl From<$elem> for Element {
375 fn from(elem: $elem) -> Element {
376 Element::builder($name)
377 .ns($ns)
378 $(
379 .attr($attr_name, elem.$attr)
380 )*
381 .append($codec::encode(&elem.$text_ident))
382 .build()
383 }
384 }
385 );
386}
387
388macro_rules! generate_element_with_children {
389 ($(#[$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),+]) => (
390 $(#[$meta])*
391 #[derive(Debug, Clone)]
392 pub struct $elem {
393 $(
394 $(#[$attr_meta])*
395 pub $attr: $attr_type,
396 )*
397 $(
398 $(#[$child_meta])*
399 pub $child_ident: Vec<$child_type>,
400 )*
401 }
402
403 impl TryFrom<Element> for $elem {
404 type Err = Error;
405
406 fn try_from(elem: Element) -> Result<$elem, Error> {
407 check_self!(elem, $name, $ns);
408 check_no_unknown_attributes!(elem, $name, [$($attr_name),*]);
409 let mut parsed_children = vec!();
410 for child in elem.children() {
411 $(
412 if child.is($child_name, $child_ns) {
413 let parsed_child = $child_constructor::try_from(child.clone())?;
414 parsed_children.push(parsed_child);
415 continue;
416 }
417 )*
418 return Err(Error::ParseError(concat!("Unknown child in ", $name, " element.")));
419 }
420 Ok($elem {
421 $(
422 $attr: get_attr!(elem, $attr_name, $attr_action),
423 )*
424 $(
425 $child_ident: parsed_children,
426 )*
427 })
428 }
429 }
430
431 impl From<$elem> for Element {
432 fn from(elem: $elem) -> Element {
433 Element::builder($name)
434 .ns($ns)
435 $(
436 .attr($attr_name, elem.$attr)
437 )*
438 $(
439 .append(elem.$child_ident)
440 )*
441 .build()
442 }
443 }
444 );
445}