// Copyright (c) 2017-2018 Emmanuel Gil Peyrot // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. macro_rules! get_attr { ($elem:ident, $attr:tt, $type:tt) => { get_attr!( $elem, $attr, $type, value, value.parse().map_err(xso::error::Error::text_parse_error)? ) }; ($elem:ident, $attr:tt, Option, $value:ident, $func:expr) => { match $elem.attr($attr) { Some($value) => Some($func), None => None, } }; ($elem:ident, $attr:tt, Required, $value:ident, $func:expr) => { match $elem.attr($attr) { Some($value) => $func, None => { return Err(xso::error::Error::Other( concat!("Required attribute '", $attr, "' missing.").into(), ) .into()); } } }; ($elem:ident, $attr:tt, Default, $value:ident, $func:expr) => { match $elem.attr($attr) { Some($value) => $func, None => ::core::default::Default::default(), } }; } macro_rules! generate_attribute { ($(#[$meta:meta])* $elem:ident, $name:tt, {$($(#[$a_meta:meta])* $a:ident => $b:tt),+$(,)?}) => ( $(#[$meta])* #[derive(Debug, Clone, PartialEq)] pub enum $elem { $( $(#[$a_meta])* $a ),+ } impl ::core::str::FromStr for $elem { type Err = xso::error::Error; fn from_str(s: &str) -> Result<$elem, xso::error::Error> { Ok(match s { $($b => $elem::$a),+, _ => return Err(xso::error::Error::Other(concat!("Unknown value for '", $name, "' attribute.")).into()), }) } } impl ::xso::FromXmlText for $elem { fn from_xml_text(s: String) -> Result<$elem, xso::error::Error> { s.parse().map_err(xso::error::Error::text_parse_error) } } impl core::fmt::Display for $elem { fn fmt(&self, fmt: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { write!(fmt, "{}", match self { $($elem::$a => $b),+ }) } } impl ::xso::AsXmlText for $elem { fn as_xml_text(&self) -> Result<::alloc::borrow::Cow<'_, str>, xso::error::Error> { match self { $( $elem::$a => Ok(::alloc::borrow::Cow::Borrowed($b)) ),+ } } } impl ::minidom::IntoAttributeValue for $elem { fn into_attribute_value(self) -> Option { Some(String::from(match self { $($elem::$a => $b),+ })) } } ); ($(#[$meta:meta])* $elem:ident, $name:tt, {$($(#[$a_meta:meta])* $a:ident => $b:tt),+$(,)?}, Default = $default:ident) => ( $(#[$meta])* #[derive(Debug, Clone, PartialEq)] pub enum $elem { $( $(#[$a_meta])* $a ),+ } impl ::core::str::FromStr for $elem { type Err = xso::error::Error; fn from_str(s: &str) -> Result<$elem, xso::error::Error> { Ok(match s { $($b => $elem::$a),+, _ => return Err(xso::error::Error::Other(concat!("Unknown value for '", $name, "' attribute.")).into()), }) } } impl ::xso::FromXmlText for $elem { fn from_xml_text(s: String) -> Result<$elem, xso::error::Error> { s.parse().map_err(xso::error::Error::text_parse_error) } } impl ::xso::AsXmlText for $elem { fn as_xml_text(&self) -> Result, xso::error::Error> { Ok(alloc::borrow::Cow::Borrowed(match self { $($elem::$a => $b),+ })) } #[allow(unreachable_patterns)] fn as_optional_xml_text(&self) -> Result>, xso::error::Error> { Ok(Some(alloc::borrow::Cow::Borrowed(match self { $elem::$default => return Ok(None), $($elem::$a => $b),+ }))) } } impl ::minidom::IntoAttributeValue for $elem { #[allow(unreachable_patterns)] fn into_attribute_value(self) -> Option { Some(String::from(match self { $elem::$default => return None, $($elem::$a => $b),+ })) } } impl ::core::default::Default for $elem { fn default() -> $elem { $elem::$default } } ); ($(#[$meta:meta])* $elem:ident, $name:tt, ($(#[$meta_symbol:meta])* $symbol:ident => $value:tt)) => ( $(#[$meta])* #[derive(Debug, Clone, PartialEq)] pub enum $elem { $(#[$meta_symbol])* $symbol, /// Value when absent. None, } impl ::core::str::FromStr for $elem { type Err = xso::error::Error; fn from_str(s: &str) -> Result { Ok(match s { $value => $elem::$symbol, _ => return Err(xso::error::Error::Other(concat!("Unknown value for '", $name, "' attribute."))), }) } } impl ::minidom::IntoAttributeValue for $elem { fn into_attribute_value(self) -> Option { match self { $elem::$symbol => Some(String::from($value)), $elem::None => None } } } impl ::core::default::Default for $elem { fn default() -> $elem { $elem::None } } impl ::xso::FromXmlText for $elem { fn from_xml_text(s: String) -> Result<$elem, xso::error::Error> { s.parse().map_err(xso::error::Error::text_parse_error) } } impl ::xso::AsXmlText for $elem { fn as_xml_text(&self) -> Result<::alloc::borrow::Cow<'_, str>, xso::error::Error> { Ok(::alloc::borrow::Cow::Borrowed($value)) } #[allow(unreachable_patterns)] fn as_optional_xml_text(&self) -> Result>, xso::error::Error> { Ok(Some(alloc::borrow::Cow::Borrowed(match self { $elem::$symbol => $value, $elem::None => return Ok(None), }))) } } ); ($(#[$meta:meta])* $elem:ident, $name:tt, $type:tt, Default = $default:expr) => ( $(#[$meta])* #[derive(Debug, Clone, PartialEq)] pub struct $elem(pub $type); impl ::core::str::FromStr for $elem { type Err = xso::error::Error; fn from_str(s: &str) -> Result { Ok($elem($type::from_str(s).map_err(xso::error::Error::text_parse_error)?)) } } impl ::minidom::IntoAttributeValue for $elem { fn into_attribute_value(self) -> Option { match self { $elem($default) => None, $elem(value) => Some(format!("{}", value)), } } } impl ::core::default::Default for $elem { fn default() -> $elem { $elem($default) } } impl ::xso::FromXmlText for $elem { fn from_xml_text(s: String) -> Result<$elem, xso::error::Error> { s.parse().map_err(xso::error::Error::text_parse_error) } } impl ::xso::AsXmlText for $elem { fn as_xml_text(&self) -> Result<::alloc::borrow::Cow<'_, str>, xso::error::Error> { Ok(::alloc::borrow::Cow::Owned(format!("{}", self.0))) } fn as_optional_xml_text(&self) -> Result>, xso::error::Error> { match self.0 { $default => Ok(None), _ => Ok(Some(::alloc::borrow::Cow::Owned(format!("{}", self.0)))), } } } ); } macro_rules! generate_attribute_enum { ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, $attr:tt, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+$(,)?}) => ( $(#[$meta])* #[derive(Debug, Clone, PartialEq)] pub enum $elem { $( $(#[$enum_meta])* $enum ),+ } impl ::core::convert::TryFrom for $elem { type Error = xso::error::FromElementError; fn try_from(elem: minidom::Element) -> Result<$elem, xso::error::FromElementError> { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); check_no_unknown_attributes!(elem, $name, [$attr]); Ok(match get_attr!(elem, $attr, Required) { $($enum_name => $elem::$enum,)+ _ => return Err(xso::error::Error::Other(concat!("Invalid ", $name, " ", $attr, " value.")).into()), }) } } impl ::xso::FromXml for $elem { type Builder = ::xso::minidom_compat::FromEventsViaElement<$elem>; fn from_events( qname: ::xso::exports::rxml::QName, attrs: ::xso::exports::rxml::AttrMap, ) -> Result { if qname.0 != crate::ns::$ns || qname.1 != $name { return Err(::xso::error::FromEventsError::Mismatch { name: qname, attrs, }) } Self::Builder::new(qname, attrs) } } impl From<$elem> for minidom::Element { fn from(elem: $elem) -> minidom::Element { minidom::Element::builder($name, crate::ns::$ns) .attr($attr, match elem { $($elem::$enum => $enum_name,)+ }) .build() } } impl ::xso::AsXml for $elem { type ItemIter<'x> = ::xso::minidom_compat::AsItemsViaElement<'x>; fn as_xml_iter(&self) -> Result, ::xso::error::Error> { ::xso::minidom_compat::AsItemsViaElement::new(self.clone()) } } ); } macro_rules! check_self { ($elem:ident, $name:tt, $ns:ident) => { check_self!($elem, $name, $ns, $name); }; ($elem:ident, $name:tt, $ns:ident, $pretty_name:tt) => { if !$elem.is($name, crate::ns::$ns) { return Err(xso::error::FromElementError::Mismatch($elem)); } }; } macro_rules! check_ns_only { ($elem:ident, $name:tt, $ns:ident) => { if !$elem.has_ns(crate::ns::$ns) { return Err(xso::error::Error::Other( concat!("This is not a ", $name, " element.").into(), ) .into()); } }; } macro_rules! check_no_children { ($elem:ident, $name:tt) => { #[cfg(not(feature = "disable-validation"))] for _ in $elem.children() { return Err(xso::error::Error::Other( concat!("Unknown child in ", $name, " element.").into(), ) .into()); } }; } macro_rules! check_no_attributes { ($elem:ident, $name:tt) => { #[cfg(not(feature = "disable-validation"))] for _ in $elem.attrs() { return Err(xso::error::Error::Other( concat!("Unknown attribute in ", $name, " element.").into(), ) .into()); } }; } macro_rules! check_no_unknown_attributes { ($elem:ident, $name:tt, [$($attr:tt),*]) => ( #[cfg(not(feature = "disable-validation"))] for (_attr, _) in $elem.attrs() { $( if _attr == $attr { continue; } )* return Err(xso::error::Error::Other(concat!("Unknown attribute in ", $name, " element.")).into()); } ); } macro_rules! generate_id { ($(#[$meta:meta])* $elem:ident) => ( $(#[$meta])* #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $elem(pub String); impl ::core::str::FromStr for $elem { type Err = xso::error::Error; fn from_str(s: &str) -> Result<$elem, xso::error::Error> { // TODO: add a way to parse that differently when needed. Ok($elem(String::from(s))) } } impl ::xso::FromXmlText for $elem { fn from_xml_text(s: String) -> Result<$elem, xso::error::Error> { Ok(Self(s)) } } impl ::xso::AsXmlText for $elem { fn as_xml_text(&self) ->Result<::alloc::borrow::Cow<'_, str>, xso::error::Error> { Ok(::alloc::borrow::Cow::Borrowed(self.0.as_str())) } } impl ::minidom::IntoAttributeValue for $elem { fn into_attribute_value(self) -> Option { Some(self.0) } } ); } macro_rules! generate_elem_id { ($(#[$meta:meta])* $elem:ident, $name:literal, $ns:ident) => ( generate_elem_id!($(#[$meta])* $elem, $name, $ns, String); impl ::core::str::FromStr for $elem { type Err = xso::error::Error; fn from_str(s: &str) -> Result<$elem, xso::error::Error> { // TODO: add a way to parse that differently when needed. Ok($elem(String::from(s))) } } ); ($(#[$meta:meta])* $elem:ident, $name:literal, $ns:ident, $type:ty) => ( $(#[$meta])* #[derive(xso::FromXml, xso::AsXml, Debug, Clone, PartialEq, Eq, Hash)] #[xml(namespace = crate::ns::$ns, name = $name)] pub struct $elem(#[xml(text)] pub $type); ); } #[cfg(test)] macro_rules! assert_size ( ($t:ty, $sz:expr) => ( assert_eq!(::core::mem::size_of::<$t>(), $sz); ); );