xmpp_parsers/util/
macros.rs

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