macros.rs

  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, $value:ident, $func:expr) => (
 12        match $elem.attr($attr) {
 13            Some($value) => Some($func),
 14            None => None,
 15        }
 16    );
 17    ($elem:ident, $attr:tt, required, $value:ident, $func:expr) => (
 18        match $elem.attr($attr) {
 19            Some($value) => $func,
 20            None => return Err(Error::ParseError(concat!("Required attribute '", $attr, "' missing."))),
 21        }
 22    );
 23    ($elem:ident, $attr:tt, default, $value:ident, $func:expr) => (
 24        match $elem.attr($attr) {
 25            Some($value) => $func,
 26            None => Default::default(),
 27        }
 28    );
 29}
 30
 31macro_rules! generate_attribute {
 32    ($elem:ident, $name:tt, {$($a:ident => $b:tt),+,}) => (
 33        generate_attribute!($elem, $name, {$($a => $b),+});
 34    );
 35    ($elem:ident, $name:tt, {$($a:ident => $b:tt),+,}, Default = $default:ident) => (
 36        generate_attribute!($elem, $name, {$($a => $b),+}, Default = $default);
 37    );
 38    ($elem:ident, $name:tt, {$($a:ident => $b:tt),+}) => (
 39        #[derive(Debug, Clone, PartialEq)]
 40        pub enum $elem {
 41            $(
 42                #[doc=$b]
 43                #[doc="value for this attribute."]
 44                $a
 45            ),+
 46        }
 47        impl FromStr for $elem {
 48            type Err = Error;
 49            fn from_str(s: &str) -> Result<$elem, Error> {
 50                Ok(match s {
 51                    $($b => $elem::$a),+,
 52                    _ => return Err(Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
 53                })
 54            }
 55        }
 56        impl IntoAttributeValue for $elem {
 57            fn into_attribute_value(self) -> Option<String> {
 58                Some(String::from(match self {
 59                    $($elem::$a => $b),+
 60                }))
 61            }
 62        }
 63    );
 64    ($elem:ident, $name:tt, {$($a:ident => $b:tt),+}, Default = $default:ident) => (
 65        #[derive(Debug, Clone, PartialEq)]
 66        pub enum $elem {
 67            $(
 68                #[doc=$b]
 69                #[doc="value for this attribute."]
 70                $a
 71            ),+
 72        }
 73        impl FromStr for $elem {
 74            type Err = Error;
 75            fn from_str(s: &str) -> Result<$elem, Error> {
 76                Ok(match s {
 77                    $($b => $elem::$a),+,
 78                    _ => return Err(Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
 79                })
 80            }
 81        }
 82        impl IntoAttributeValue for $elem {
 83            #[allow(unreachable_patterns)]
 84            fn into_attribute_value(self) -> Option<String> {
 85                Some(String::from(match self {
 86                    $elem::$default => return None,
 87                    $($elem::$a => $b),+
 88                }))
 89            }
 90        }
 91        impl Default for $elem {
 92            fn default() -> $elem {
 93                $elem::$default
 94            }
 95        }
 96    );
 97}
 98
 99macro_rules! generate_element_enum {
100    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+,}) => (
101        generate_element_enum!($(#[$meta])* $elem, $name, $ns, {$($(#[$enum_meta])* $enum => $enum_name),+});
102    );
103    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+}) => (
104        $(#[$meta])*
105        #[derive(Debug, Clone, PartialEq)]
106        pub enum $elem {
107            $(
108            $(#[$enum_meta])*
109            $enum
110            ),+
111        }
112        impl TryFrom<Element> for $elem {
113            type Err = Error;
114            fn try_from(elem: Element) -> Result<$elem, Error> {
115                check_ns_only!(elem, $name, $ns);
116                check_no_children!(elem, $name);
117                check_no_attributes!(elem, $name);
118                Ok(match elem.name() {
119                    $($enum_name => $elem::$enum,)+
120                    _ => return Err(Error::ParseError(concat!("This is not a ", $name, " element."))),
121                })
122            }
123        }
124        impl From<$elem> for Element {
125            fn from(elem: $elem) -> Element {
126                Element::builder(match elem {
127                    $($elem::$enum => $enum_name,)+
128                }).ns($ns)
129                  .build()
130            }
131        }
132    );
133}
134
135macro_rules! check_self {
136    ($elem:ident, $name:tt, $ns:expr) => (
137        check_self!($elem, $name, $ns, $name);
138    );
139    ($elem:ident, $name:tt, $ns:expr, $pretty_name:tt) => (
140        if !$elem.is($name, $ns) {
141            return Err(Error::ParseError(concat!("This is not a ", $pretty_name, " element.")));
142        }
143    );
144}
145
146macro_rules! check_ns_only {
147    ($elem:ident, $name:tt, $ns:expr) => (
148        if !$elem.has_ns($ns) {
149            return Err(Error::ParseError(concat!("This is not a ", $name, " element.")));
150        }
151    );
152}
153
154macro_rules! check_no_children {
155    ($elem:ident, $name:tt) => (
156        for _ in $elem.children() {
157            return Err(Error::ParseError(concat!("Unknown child in ", $name, " element.")));
158        }
159    );
160}
161
162macro_rules! check_no_attributes {
163    ($elem:ident, $name:tt) => (
164        check_no_unknown_attributes!($elem, $name, []);
165    );
166}
167
168macro_rules! check_no_unknown_attributes {
169    ($elem:ident, $name:tt, [$($attr:tt),*]) => (
170        for (_attr, _) in $elem.attrs() {
171            $(
172            if _attr == $attr {
173                continue;
174            }
175            )*
176            return Err(Error::ParseError(concat!("Unknown attribute in ", $name, " element.")));
177        }
178    );
179}
180
181macro_rules! generate_empty_element {
182    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr) => (
183        $(#[$meta])*
184        #[derive(Debug, Clone)]
185        pub struct $elem;
186
187        impl TryFrom<Element> for $elem {
188            type Err = Error;
189
190            fn try_from(elem: Element) -> Result<$elem, Error> {
191                check_self!(elem, $name, $ns);
192                check_no_children!(elem, $name);
193                check_no_attributes!(elem, $name);
194                Ok($elem)
195            }
196        }
197
198        impl From<$elem> for Element {
199            fn from(_: $elem) -> Element {
200                Element::builder($name)
201                        .ns($ns)
202                        .build()
203            }
204        }
205    );
206}
207
208macro_rules! generate_element_with_only_attributes {
209    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => (
210        generate_element_with_only_attributes!($(#[$meta])* $elem, $name, $ns, [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*]);
211    );
212    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => (
213        $(#[$meta])*
214        #[derive(Debug, Clone)]
215        pub struct $elem {
216            $(
217            $(#[$attr_meta])*
218            pub $attr: $attr_type,
219            )*
220        }
221
222        impl TryFrom<Element> for $elem {
223            type Err = Error;
224
225            fn try_from(elem: Element) -> Result<$elem, Error> {
226                check_self!(elem, $name, $ns);
227                check_no_children!(elem, $name);
228                check_no_unknown_attributes!(elem, $name, [$($attr_name),*]);
229                Ok($elem {
230                    $(
231                    $attr: get_attr!(elem, $attr_name, $attr_action),
232                    )*
233                })
234            }
235        }
236
237        impl From<$elem> for Element {
238            fn from(elem: $elem) -> Element {
239                Element::builder($name)
240                        .ns($ns)
241                        $(
242                        .attr($attr_name, elem.$attr)
243                        )*
244                        .build()
245            }
246        }
247    );
248}
249
250macro_rules! generate_id {
251    ($elem:ident) => (
252        #[derive(Debug, Clone, PartialEq, Eq, Hash)]
253        pub struct $elem(pub String);
254        impl FromStr for $elem {
255            type Err = Error;
256            fn from_str(s: &str) -> Result<$elem, Error> {
257                // TODO: add a way to parse that differently when needed.
258                Ok($elem(String::from(s)))
259            }
260        }
261        impl IntoAttributeValue for $elem {
262            fn into_attribute_value(self) -> Option<String> {
263                Some(self.0)
264            }
265        }
266    );
267}
268
269macro_rules! generate_elem_id {
270    ($elem:ident, $name:tt, $ns:expr) => (
271        #[derive(Debug, Clone, PartialEq, Eq, Hash)]
272        pub struct $elem(pub String);
273        impl FromStr for $elem {
274            type Err = Error;
275            fn from_str(s: &str) -> Result<$elem, Error> {
276                // TODO: add a way to parse that differently when needed.
277                Ok($elem(String::from(s)))
278            }
279        }
280        impl From<$elem> for Element {
281            fn from(elem: $elem) -> Element {
282                Element::builder($name)
283                        .ns($ns)
284                        .append(elem.0)
285                        .build()
286            }
287        }
288    );
289}
290
291macro_rules! generate_element_with_text {
292    ($(#[$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 >) => (
293        $(#[$meta])*
294        #[derive(Debug, Clone)]
295        pub struct $elem {
296            $(
297            $(#[$attr_meta])*
298            pub $attr: $attr_type,
299            )*
300            pub $text_ident: $text_type,
301        }
302
303        impl TryFrom<Element> for $elem {
304            type Err = Error;
305
306            fn try_from(elem: Element) -> Result<$elem, Error> {
307                check_self!(elem, $name, $ns);
308                check_no_children!(elem, $name);
309                check_no_unknown_attributes!(elem, $name, [$($attr_name),*]);
310                Ok($elem {
311                    $(
312                    $attr: get_attr!(elem, $attr_name, $attr_action),
313                    )*
314                    $text_ident: $codec::decode(&elem.text())?,
315                })
316            }
317        }
318
319        impl From<$elem> for Element {
320            fn from(elem: $elem) -> Element {
321                Element::builder($name)
322                        .ns($ns)
323                        $(
324                        .attr($attr_name, elem.$attr)
325                        )*
326                        .append($codec::encode(&elem.$text_ident))
327                        .build()
328            }
329        }
330    );
331}
332
333macro_rules! generate_element_with_children {
334    ($(#[$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),+]) => (
335        $(#[$meta])*
336        #[derive(Debug, Clone)]
337        pub struct $elem {
338            $(
339            $(#[$attr_meta])*
340            pub $attr: $attr_type,
341            )*
342            $(
343            $(#[$child_meta])*
344            pub $child_ident: Vec<$child_type>,
345            )*
346        }
347
348        impl TryFrom<Element> for $elem {
349            type Err = Error;
350
351            fn try_from(elem: Element) -> Result<$elem, Error> {
352                check_self!(elem, $name, $ns);
353                check_no_unknown_attributes!(elem, $name, [$($attr_name),*]);
354                let mut parsed_children = vec!();
355                for child in elem.children() {
356                    $(
357                    if child.is($child_name, $child_ns) {
358                        let parsed_child = $child_constructor::try_from(child.clone())?;
359                        parsed_children.push(parsed_child);
360                        continue;
361                    }
362                    )*
363                    return Err(Error::ParseError(concat!("Unknown child in ", $name, " element.")));
364                }
365                Ok($elem {
366                    $(
367                    $attr: get_attr!(elem, $attr_name, $attr_action),
368                    )*
369                    $(
370                    $child_ident: parsed_children,
371                    )*
372                })
373            }
374        }
375
376        impl From<$elem> for Element {
377            fn from(elem: $elem) -> Element {
378                Element::builder($name)
379                        .ns($ns)
380                        $(
381                        .attr($attr_name, elem.$attr)
382                        )*
383                        $(
384                        .append(elem.$child_ident)
385                        )*
386                        .build()
387            }
388        }
389    );
390}