Rename the xmpp-parsers directory to parsers
This doesn’t change anything to the name of the crate, just makes autocompletion easier by not sharing the same prefix as the xmpp crate.
This commit is contained in:
parent
204b1f38eb
commit
9410849d7a
84 changed files with 2 additions and 2 deletions
105
parsers/src/util/error.rs
Normal file
105
parsers/src/util/error.rs
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
// Copyright (c) 2017-2018 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
|
||||
//
|
||||
// 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/.
|
||||
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt;
|
||||
|
||||
/// Contains one of the potential errors triggered while parsing an
|
||||
/// [Element](../struct.Element.html) into a specialised struct.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// The usual error when parsing something.
|
||||
///
|
||||
/// TODO: use a structured error so the user can report it better, instead
|
||||
/// of a freeform string.
|
||||
ParseError(&'static str),
|
||||
|
||||
/// Generated when some base64 content fails to decode, usually due to
|
||||
/// extra characters.
|
||||
Base64Error(base64::DecodeError),
|
||||
|
||||
/// Generated when text which should be an integer fails to parse.
|
||||
ParseIntError(std::num::ParseIntError),
|
||||
|
||||
/// Generated when text which should be a string fails to parse.
|
||||
ParseStringError(std::string::ParseError),
|
||||
|
||||
/// Generated when text which should be an IP address (IPv4 or IPv6) fails
|
||||
/// to parse.
|
||||
ParseAddrError(std::net::AddrParseError),
|
||||
|
||||
/// Generated when text which should be a [JID](../../jid/struct.Jid.html)
|
||||
/// fails to parse.
|
||||
JidParseError(jid::JidParseError),
|
||||
|
||||
/// Generated when text which should be a
|
||||
/// [DateTime](../date/struct.DateTime.html) fails to parse.
|
||||
ChronoParseError(chrono::ParseError),
|
||||
}
|
||||
|
||||
impl StdError for Error {
|
||||
fn cause(&self) -> Option<&dyn StdError> {
|
||||
match self {
|
||||
Error::ParseError(_) => None,
|
||||
Error::Base64Error(e) => Some(e),
|
||||
Error::ParseIntError(e) => Some(e),
|
||||
Error::ParseStringError(e) => Some(e),
|
||||
Error::ParseAddrError(e) => Some(e),
|
||||
Error::JidParseError(e) => Some(e),
|
||||
Error::ChronoParseError(e) => Some(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Error::ParseError(s) => write!(fmt, "parse error: {}", s),
|
||||
Error::Base64Error(e) => write!(fmt, "base64 error: {}", e),
|
||||
Error::ParseIntError(e) => write!(fmt, "integer parsing error: {}", e),
|
||||
Error::ParseStringError(e) => write!(fmt, "string parsing error: {}", e),
|
||||
Error::ParseAddrError(e) => write!(fmt, "IP address parsing error: {}", e),
|
||||
Error::JidParseError(e) => write!(fmt, "JID parsing error: {}", e),
|
||||
Error::ChronoParseError(e) => write!(fmt, "time parsing error: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<base64::DecodeError> for Error {
|
||||
fn from(err: base64::DecodeError) -> Error {
|
||||
Error::Base64Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::num::ParseIntError> for Error {
|
||||
fn from(err: std::num::ParseIntError) -> Error {
|
||||
Error::ParseIntError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::string::ParseError> for Error {
|
||||
fn from(err: std::string::ParseError) -> Error {
|
||||
Error::ParseStringError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::net::AddrParseError> for Error {
|
||||
fn from(err: std::net::AddrParseError) -> Error {
|
||||
Error::ParseAddrError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<jid::JidParseError> for Error {
|
||||
fn from(err: jid::JidParseError) -> Error {
|
||||
Error::JidParseError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<chrono::ParseError> for Error {
|
||||
fn from(err: chrono::ParseError) -> Error {
|
||||
Error::ChronoParseError(err)
|
||||
}
|
||||
}
|
||||
122
parsers/src/util/helpers.rs
Normal file
122
parsers/src/util/helpers.rs
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
// Copyright (c) 2017 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
|
||||
//
|
||||
// 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/.
|
||||
|
||||
use crate::util::error::Error;
|
||||
use jid::Jid;
|
||||
use std::str::FromStr;
|
||||
|
||||
/// Codec for text content.
|
||||
pub struct Text;
|
||||
|
||||
impl Text {
|
||||
pub fn decode(s: &str) -> Result<String, Error> {
|
||||
Ok(s.to_owned())
|
||||
}
|
||||
|
||||
pub fn encode(string: &str) -> Option<String> {
|
||||
Some(string.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
/// Codec for plain text content.
|
||||
pub struct PlainText;
|
||||
|
||||
impl PlainText {
|
||||
pub fn decode(s: &str) -> Result<Option<String>, Error> {
|
||||
Ok(match s {
|
||||
"" => None,
|
||||
text => Some(text.to_owned()),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn encode(string: &Option<String>) -> Option<String> {
|
||||
string.as_ref().map(ToOwned::to_owned)
|
||||
}
|
||||
}
|
||||
|
||||
/// Codec for trimmed plain text content.
|
||||
pub struct TrimmedPlainText;
|
||||
|
||||
impl TrimmedPlainText {
|
||||
pub fn decode(s: &str) -> Result<String, Error> {
|
||||
Ok(match s.trim() {
|
||||
"" => return Err(Error::ParseError("URI missing in uri.")),
|
||||
text => text.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn encode(string: &str) -> Option<String> {
|
||||
Some(string.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
/// Codec wrapping base64 encode/decode.
|
||||
pub struct Base64;
|
||||
|
||||
impl Base64 {
|
||||
pub fn decode(s: &str) -> Result<Vec<u8>, Error> {
|
||||
Ok(base64::decode(s)?)
|
||||
}
|
||||
|
||||
pub fn encode(b: &[u8]) -> Option<String> {
|
||||
Some(base64::encode(b))
|
||||
}
|
||||
}
|
||||
|
||||
/// Codec wrapping base64 encode/decode, while ignoring whitespace characters.
|
||||
pub struct WhitespaceAwareBase64;
|
||||
|
||||
impl WhitespaceAwareBase64 {
|
||||
pub fn decode(s: &str) -> Result<Vec<u8>, Error> {
|
||||
let s: String = s
|
||||
.chars()
|
||||
.filter(|ch| *ch != ' ' && *ch != '\n' && *ch != '\t')
|
||||
.collect();
|
||||
Ok(base64::decode(&s)?)
|
||||
}
|
||||
|
||||
pub fn encode(b: &[u8]) -> Option<String> {
|
||||
Some(base64::encode(b))
|
||||
}
|
||||
}
|
||||
|
||||
/// Codec for colon-separated bytes of uppercase hexadecimal.
|
||||
pub struct ColonSeparatedHex;
|
||||
|
||||
impl ColonSeparatedHex {
|
||||
pub fn decode(s: &str) -> Result<Vec<u8>, Error> {
|
||||
let mut bytes = vec![];
|
||||
for i in 0..(1 + s.len()) / 3 {
|
||||
let byte = u8::from_str_radix(&s[3 * i..3 * i + 2], 16)?;
|
||||
if 3 * i + 2 < s.len() {
|
||||
assert_eq!(&s[3 * i + 2..3 * i + 3], ":");
|
||||
}
|
||||
bytes.push(byte);
|
||||
}
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
pub fn encode(b: &[u8]) -> Option<String> {
|
||||
let mut bytes = vec![];
|
||||
for byte in b {
|
||||
bytes.push(format!("{:02X}", byte));
|
||||
}
|
||||
Some(bytes.join(":"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Codec for a JID.
|
||||
pub struct JidCodec;
|
||||
|
||||
impl JidCodec {
|
||||
pub fn decode(s: &str) -> Result<Jid, Error> {
|
||||
Ok(Jid::from_str(s)?)
|
||||
}
|
||||
|
||||
pub fn encode(jid: &Jid) -> Option<String> {
|
||||
Some(jid.to_string())
|
||||
}
|
||||
}
|
||||
767
parsers/src/util/macros.rs
Normal file
767
parsers/src/util/macros.rs
Normal file
|
|
@ -0,0 +1,767 @@
|
|||
// Copyright (c) 2017-2018 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
|
||||
//
|
||||
// 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()?)
|
||||
};
|
||||
($elem:ident, $attr:tt, OptionEmpty, $value:ident, $func:expr) => {
|
||||
match $elem.attr($attr) {
|
||||
Some("") => None,
|
||||
Some($value) => Some($func),
|
||||
None => None,
|
||||
}
|
||||
};
|
||||
($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(crate::util::error::Error::ParseError(concat!(
|
||||
"Required attribute '",
|
||||
$attr,
|
||||
"' missing."
|
||||
)));
|
||||
}
|
||||
}
|
||||
};
|
||||
($elem:ident, $attr:tt, RequiredNonEmpty, $value:ident, $func:expr) => {
|
||||
match $elem.attr($attr) {
|
||||
Some("") => {
|
||||
return Err(crate::util::error::Error::ParseError(concat!(
|
||||
"Required attribute '",
|
||||
$attr,
|
||||
"' must not be empty."
|
||||
)));
|
||||
}
|
||||
Some($value) => $func,
|
||||
None => {
|
||||
return Err(crate::util::error::Error::ParseError(concat!(
|
||||
"Required attribute '",
|
||||
$attr,
|
||||
"' missing."
|
||||
)));
|
||||
}
|
||||
}
|
||||
};
|
||||
($elem:ident, $attr:tt, Default, $value:ident, $func:expr) => {
|
||||
match $elem.attr($attr) {
|
||||
Some($value) => $func,
|
||||
None => ::std::default::Default::default(),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! generate_attribute {
|
||||
($(#[$meta:meta])* $elem:ident, $name:tt, {$($(#[$a_meta:meta])* $a:ident => $b:tt),+,}) => (
|
||||
generate_attribute!($(#[$meta])* $elem, $name, {$($(#[$a_meta])* $a => $b),+});
|
||||
);
|
||||
($(#[$meta:meta])* $elem:ident, $name:tt, {$($(#[$a_meta:meta])* $a:ident => $b:tt),+,}, Default = $default:ident) => (
|
||||
generate_attribute!($(#[$meta])* $elem, $name, {$($(#[$a_meta])* $a => $b),+}, Default = $default);
|
||||
);
|
||||
($(#[$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 ::std::str::FromStr for $elem {
|
||||
type Err = crate::util::error::Error;
|
||||
fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> {
|
||||
Ok(match s {
|
||||
$($b => $elem::$a),+,
|
||||
_ => return Err(crate::util::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for $elem {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
||||
write!(fmt, "{}", match self {
|
||||
$($elem::$a => $b),+
|
||||
})
|
||||
}
|
||||
}
|
||||
impl ::minidom::IntoAttributeValue for $elem {
|
||||
fn into_attribute_value(self) -> Option<String> {
|
||||
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 ::std::str::FromStr for $elem {
|
||||
type Err = crate::util::error::Error;
|
||||
fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> {
|
||||
Ok(match s {
|
||||
$($b => $elem::$a),+,
|
||||
_ => return Err(crate::util::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl ::minidom::IntoAttributeValue for $elem {
|
||||
#[allow(unreachable_patterns)]
|
||||
fn into_attribute_value(self) -> Option<String> {
|
||||
Some(String::from(match self {
|
||||
$elem::$default => return None,
|
||||
$($elem::$a => $b),+
|
||||
}))
|
||||
}
|
||||
}
|
||||
impl ::std::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 ::std::str::FromStr for $elem {
|
||||
type Err = crate::util::error::Error;
|
||||
fn from_str(s: &str) -> Result<Self, crate::util::error::Error> {
|
||||
Ok(match s {
|
||||
$value => $elem::$symbol,
|
||||
_ => return Err(crate::util::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl ::minidom::IntoAttributeValue for $elem {
|
||||
fn into_attribute_value(self) -> Option<String> {
|
||||
match self {
|
||||
$elem::$symbol => Some(String::from($value)),
|
||||
$elem::None => None
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ::std::default::Default for $elem {
|
||||
fn default() -> $elem {
|
||||
$elem::None
|
||||
}
|
||||
}
|
||||
);
|
||||
($(#[$meta:meta])* $elem:ident, $name:tt, bool) => (
|
||||
$(#[$meta])*
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum $elem {
|
||||
/// True value, represented by either 'true' or '1'.
|
||||
True,
|
||||
/// False value, represented by either 'false' or '0'.
|
||||
False,
|
||||
}
|
||||
impl ::std::str::FromStr for $elem {
|
||||
type Err = crate::util::error::Error;
|
||||
fn from_str(s: &str) -> Result<Self, crate::util::error::Error> {
|
||||
Ok(match s {
|
||||
"true" | "1" => $elem::True,
|
||||
"false" | "0" => $elem::False,
|
||||
_ => return Err(crate::util::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl ::minidom::IntoAttributeValue for $elem {
|
||||
fn into_attribute_value(self) -> Option<String> {
|
||||
match self {
|
||||
$elem::True => Some(String::from("true")),
|
||||
$elem::False => None
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ::std::default::Default for $elem {
|
||||
fn default() -> $elem {
|
||||
$elem::False
|
||||
}
|
||||
}
|
||||
);
|
||||
($(#[$meta:meta])* $elem:ident, $name:tt, $type:tt, Default = $default:expr) => (
|
||||
$(#[$meta])*
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct $elem(pub $type);
|
||||
impl ::std::str::FromStr for $elem {
|
||||
type Err = crate::util::error::Error;
|
||||
fn from_str(s: &str) -> Result<Self, crate::util::error::Error> {
|
||||
Ok($elem($type::from_str(s)?))
|
||||
}
|
||||
}
|
||||
impl ::minidom::IntoAttributeValue for $elem {
|
||||
fn into_attribute_value(self) -> Option<String> {
|
||||
match self {
|
||||
$elem($default) => None,
|
||||
$elem(value) => Some(format!("{}", value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ::std::default::Default for $elem {
|
||||
fn default() -> $elem {
|
||||
$elem($default)
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
macro_rules! generate_element_enum {
|
||||
($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+,}) => (
|
||||
generate_element_enum!($(#[$meta])* $elem, $name, $ns, {$($(#[$enum_meta])* $enum => $enum_name),+});
|
||||
);
|
||||
($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+}) => (
|
||||
$(#[$meta])*
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum $elem {
|
||||
$(
|
||||
$(#[$enum_meta])*
|
||||
$enum
|
||||
),+
|
||||
}
|
||||
impl ::std::convert::TryFrom<crate::Element> for $elem {
|
||||
type Error = crate::util::error::Error;
|
||||
fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> {
|
||||
check_ns_only!(elem, $name, $ns);
|
||||
check_no_children!(elem, $name);
|
||||
check_no_attributes!(elem, $name);
|
||||
Ok(match elem.name() {
|
||||
$($enum_name => $elem::$enum,)+
|
||||
_ => return Err(crate::util::error::Error::ParseError(concat!("This is not a ", $name, " element."))),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl From<$elem> for crate::Element {
|
||||
fn from(elem: $elem) -> crate::Element {
|
||||
crate::Element::builder(
|
||||
match elem {
|
||||
$($elem::$enum => $enum_name,)+
|
||||
},
|
||||
crate::ns::$ns,
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
macro_rules! generate_attribute_enum {
|
||||
($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, $attr:tt, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+,}) => (
|
||||
generate_attribute_enum!($(#[$meta])* $elem, $name, $ns, $attr, {$($(#[$enum_meta])* $enum => $enum_name),+});
|
||||
);
|
||||
($(#[$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 ::std::convert::TryFrom<crate::Element> for $elem {
|
||||
type Error = crate::util::error::Error;
|
||||
fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> {
|
||||
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(crate::util::error::Error::ParseError(concat!("Invalid ", $name, " ", $attr, " value."))),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl From<$elem> for crate::Element {
|
||||
fn from(elem: $elem) -> crate::Element {
|
||||
crate::Element::builder($name, crate::ns::$ns)
|
||||
.attr($attr, match elem {
|
||||
$($elem::$enum => $enum_name,)+
|
||||
})
|
||||
.build()
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
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(crate::util::error::Error::ParseError(concat!(
|
||||
"This is not a ",
|
||||
$pretty_name,
|
||||
" element."
|
||||
)));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! check_ns_only {
|
||||
($elem:ident, $name:tt, $ns:ident) => {
|
||||
if !$elem.has_ns(crate::ns::$ns) {
|
||||
return Err(crate::util::error::Error::ParseError(concat!(
|
||||
"This is not a ",
|
||||
$name,
|
||||
" element."
|
||||
)));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! check_no_children {
|
||||
($elem:ident, $name:tt) => {
|
||||
#[cfg(not(feature = "disable-validation"))]
|
||||
for _ in $elem.children() {
|
||||
return Err(crate::util::error::Error::ParseError(concat!(
|
||||
"Unknown child in ",
|
||||
$name,
|
||||
" element."
|
||||
)));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! check_no_attributes {
|
||||
($elem:ident, $name:tt) => {
|
||||
#[cfg(not(feature = "disable-validation"))]
|
||||
for _ in $elem.attrs() {
|
||||
return Err(crate::util::error::Error::ParseError(concat!(
|
||||
"Unknown attribute in ",
|
||||
$name,
|
||||
" element."
|
||||
)));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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(crate::util::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element.")));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
macro_rules! generate_empty_element {
|
||||
($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident) => (
|
||||
$(#[$meta])*
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct $elem;
|
||||
|
||||
impl ::std::convert::TryFrom<crate::Element> for $elem {
|
||||
type Error = crate::util::error::Error;
|
||||
|
||||
fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> {
|
||||
check_self!(elem, $name, $ns);
|
||||
check_no_children!(elem, $name);
|
||||
check_no_attributes!(elem, $name);
|
||||
Ok($elem)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$elem> for crate::Element {
|
||||
fn from(_: $elem) -> crate::Element {
|
||||
crate::Element::builder($name, crate::ns::$ns)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
macro_rules! generate_id {
|
||||
($(#[$meta:meta])* $elem:ident) => (
|
||||
$(#[$meta])*
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct $elem(pub String);
|
||||
impl ::std::str::FromStr for $elem {
|
||||
type Err = crate::util::error::Error;
|
||||
fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> {
|
||||
// TODO: add a way to parse that differently when needed.
|
||||
Ok($elem(String::from(s)))
|
||||
}
|
||||
}
|
||||
impl ::minidom::IntoAttributeValue for $elem {
|
||||
fn into_attribute_value(self) -> Option<String> {
|
||||
Some(self.0)
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
macro_rules! generate_elem_id {
|
||||
($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident) => (
|
||||
generate_elem_id!($(#[$meta])* $elem, $name, $ns, String);
|
||||
impl ::std::str::FromStr for $elem {
|
||||
type Err = crate::util::error::Error;
|
||||
fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> {
|
||||
// TODO: add a way to parse that differently when needed.
|
||||
Ok($elem(String::from(s)))
|
||||
}
|
||||
}
|
||||
);
|
||||
($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, $type:ty) => (
|
||||
$(#[$meta])*
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct $elem(pub $type);
|
||||
impl ::std::convert::TryFrom<crate::Element> for $elem {
|
||||
type Error = crate::util::error::Error;
|
||||
fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> {
|
||||
check_self!(elem, $name, $ns);
|
||||
check_no_children!(elem, $name);
|
||||
check_no_attributes!(elem, $name);
|
||||
// TODO: add a way to parse that differently when needed.
|
||||
Ok($elem(elem.text().parse()?))
|
||||
}
|
||||
}
|
||||
impl From<$elem> for crate::Element {
|
||||
fn from(elem: $elem) -> crate::Element {
|
||||
crate::Element::builder($name, crate::ns::$ns)
|
||||
.append(elem.0.to_string())
|
||||
.build()
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
macro_rules! decl_attr {
|
||||
(OptionEmpty, $type:ty) => (
|
||||
Option<$type>
|
||||
);
|
||||
(Option, $type:ty) => (
|
||||
Option<$type>
|
||||
);
|
||||
(Required, $type:ty) => (
|
||||
$type
|
||||
);
|
||||
(RequiredNonEmpty, $type:ty) => (
|
||||
$type
|
||||
);
|
||||
(Default, $type:ty) => (
|
||||
$type
|
||||
);
|
||||
}
|
||||
|
||||
macro_rules! start_decl {
|
||||
(Vec, $type:ty) => (
|
||||
Vec<$type>
|
||||
);
|
||||
(Option, $type:ty) => (
|
||||
Option<$type>
|
||||
);
|
||||
(Required, $type:ty) => (
|
||||
$type
|
||||
);
|
||||
(Present, $type:ty) => (
|
||||
bool
|
||||
);
|
||||
}
|
||||
|
||||
macro_rules! start_parse_elem {
|
||||
($temp:ident: Vec) => {
|
||||
let mut $temp = Vec::new();
|
||||
};
|
||||
($temp:ident: Option) => {
|
||||
let mut $temp = None;
|
||||
};
|
||||
($temp:ident: Required) => {
|
||||
let mut $temp = None;
|
||||
};
|
||||
($temp:ident: Present) => {
|
||||
let mut $temp = false;
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! do_parse {
|
||||
($elem:ident, Element) => {
|
||||
$elem.clone()
|
||||
};
|
||||
($elem:ident, String) => {
|
||||
$elem.text()
|
||||
};
|
||||
($elem:ident, $constructor:ident) => {
|
||||
$constructor::try_from($elem.clone())?
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! do_parse_elem {
|
||||
($temp:ident: Vec = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => {
|
||||
$temp.push(do_parse!($elem, $constructor));
|
||||
};
|
||||
($temp:ident: Option = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => {
|
||||
if $temp.is_some() {
|
||||
return Err(crate::util::error::Error::ParseError(concat!(
|
||||
"Element ",
|
||||
$parent_name,
|
||||
" must not have more than one ",
|
||||
$name,
|
||||
" child."
|
||||
)));
|
||||
}
|
||||
$temp = Some(do_parse!($elem, $constructor));
|
||||
};
|
||||
($temp:ident: Required = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => {
|
||||
if $temp.is_some() {
|
||||
return Err(crate::util::error::Error::ParseError(concat!(
|
||||
"Element ",
|
||||
$parent_name,
|
||||
" must not have more than one ",
|
||||
$name,
|
||||
" child."
|
||||
)));
|
||||
}
|
||||
$temp = Some(do_parse!($elem, $constructor));
|
||||
};
|
||||
($temp:ident: Present = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => {
|
||||
if $temp {
|
||||
return Err(crate::util::error::Error::ParseError(concat!(
|
||||
"Element ",
|
||||
$parent_name,
|
||||
" must not have more than one ",
|
||||
$name,
|
||||
" child."
|
||||
)));
|
||||
}
|
||||
$temp = true;
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! finish_parse_elem {
|
||||
($temp:ident: Vec = $name:tt, $parent_name:tt) => {
|
||||
$temp
|
||||
};
|
||||
($temp:ident: Option = $name:tt, $parent_name:tt) => {
|
||||
$temp
|
||||
};
|
||||
($temp:ident: Required = $name:tt, $parent_name:tt) => {
|
||||
$temp.ok_or(crate::util::error::Error::ParseError(concat!(
|
||||
"Missing child ",
|
||||
$name,
|
||||
" in ",
|
||||
$parent_name,
|
||||
" element."
|
||||
)))?
|
||||
};
|
||||
($temp:ident: Present = $name:tt, $parent_name:tt) => {
|
||||
$temp
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! generate_serialiser {
|
||||
($builder:ident, $parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => {
|
||||
$builder.append(
|
||||
crate::Element::builder($name, crate::ns::$ns)
|
||||
.append(::minidom::Node::Text($parent.$elem)),
|
||||
)
|
||||
};
|
||||
($builder:ident, $parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => {
|
||||
$builder.append_all($parent.$elem.map(|elem| {
|
||||
crate::Element::builder($name, crate::ns::$ns).append(::minidom::Node::Text(elem))
|
||||
}))
|
||||
};
|
||||
($builder:ident, $parent:ident, $elem:ident, Option, $constructor:ident, ($name:tt, *)) => {
|
||||
$builder.append_all(
|
||||
$parent
|
||||
.$elem
|
||||
.map(|elem| ::minidom::Node::Element(crate::Element::from(elem))),
|
||||
)
|
||||
};
|
||||
($builder:ident, $parent:ident, $elem:ident, Option, $constructor:ident, ($name:tt, $ns:ident)) => {
|
||||
$builder.append_all(
|
||||
$parent
|
||||
.$elem
|
||||
.map(|elem| ::minidom::Node::Element(crate::Element::from(elem))),
|
||||
)
|
||||
};
|
||||
($builder:ident, $parent:ident, $elem:ident, Vec, $constructor:ident, ($name:tt, $ns:ident)) => {
|
||||
$builder.append_all($parent.$elem.into_iter())
|
||||
};
|
||||
($builder:ident, $parent:ident, $elem:ident, Present, $constructor:ident, ($name:tt, $ns:ident)) => {
|
||||
$builder.append(::minidom::Node::Element(
|
||||
crate::Element::builder($name, crate::ns::$ns).build(),
|
||||
))
|
||||
};
|
||||
($builder:ident, $parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => {
|
||||
$builder.append(::minidom::Node::Element(crate::Element::from(
|
||||
$parent.$elem,
|
||||
)))
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! generate_child_test {
|
||||
($child:ident, $name:tt, *) => {
|
||||
$child.is($name, ::minidom::NSChoice::Any)
|
||||
};
|
||||
($child:ident, $name:tt, $ns:tt) => {
|
||||
$child.is($name, crate::ns::$ns)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! generate_element {
|
||||
($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),+,]) => (
|
||||
generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: []);
|
||||
);
|
||||
($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),+]) => (
|
||||
generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: []);
|
||||
);
|
||||
($(#[$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:tt) => $child_constructor:ident),*]) => (
|
||||
generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]);
|
||||
);
|
||||
($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:tt) => $child_constructor:ident),*]) => (
|
||||
generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]);
|
||||
);
|
||||
($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >)) => (
|
||||
generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [], text: ($(#[$text_meta])* $text_ident: $codec<$text_type>));
|
||||
);
|
||||
($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),+], text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >)) => (
|
||||
generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: [], text: ($(#[$text_meta])* $text_ident: $codec<$text_type>));
|
||||
);
|
||||
($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:tt) => $child_constructor:ident),*] $(, text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >))*) => (
|
||||
$(#[$meta])*
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct $elem {
|
||||
$(
|
||||
$(#[$attr_meta])*
|
||||
pub $attr: decl_attr!($attr_action, $attr_type),
|
||||
)*
|
||||
$(
|
||||
$(#[$child_meta])*
|
||||
pub $child_ident: start_decl!($coucou, $child_type),
|
||||
)*
|
||||
$(
|
||||
$(#[$text_meta])*
|
||||
pub $text_ident: $text_type,
|
||||
)*
|
||||
}
|
||||
|
||||
impl ::std::convert::TryFrom<crate::Element> for $elem {
|
||||
type Error = crate::util::error::Error;
|
||||
|
||||
fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> {
|
||||
check_self!(elem, $name, $ns);
|
||||
check_no_unknown_attributes!(elem, $name, [$($attr_name),*]);
|
||||
$(
|
||||
start_parse_elem!($child_ident: $coucou);
|
||||
)*
|
||||
for _child in elem.children() {
|
||||
$(
|
||||
if generate_child_test!(_child, $child_name, $child_ns) {
|
||||
do_parse_elem!($child_ident: $coucou = $child_constructor => _child, $child_name, $name);
|
||||
continue;
|
||||
}
|
||||
)*
|
||||
return Err(crate::util::error::Error::ParseError(concat!("Unknown child in ", $name, " element.")));
|
||||
}
|
||||
Ok($elem {
|
||||
$(
|
||||
$attr: get_attr!(elem, $attr_name, $attr_action),
|
||||
)*
|
||||
$(
|
||||
$child_ident: finish_parse_elem!($child_ident: $coucou = $child_name, $name),
|
||||
)*
|
||||
$(
|
||||
$text_ident: $codec::decode(&elem.text())?,
|
||||
)*
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$elem> for crate::Element {
|
||||
fn from(elem: $elem) -> crate::Element {
|
||||
let mut builder = crate::Element::builder($name, crate::ns::$ns);
|
||||
$(
|
||||
builder = builder.attr($attr_name, elem.$attr);
|
||||
)*
|
||||
$(
|
||||
builder = generate_serialiser!(builder, elem, $child_ident, $coucou, $child_constructor, ($child_name, $child_ns));
|
||||
)*
|
||||
$(
|
||||
builder = builder.append_all($codec::encode(&elem.$text_ident).map(::minidom::Node::Text).into_iter());
|
||||
)*
|
||||
|
||||
builder.build()
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
macro_rules! assert_size (
|
||||
($t:ty, $sz:expr) => (
|
||||
assert_eq!(::std::mem::size_of::<$t>(), $sz);
|
||||
);
|
||||
);
|
||||
|
||||
// TODO: move that to src/pubsub/mod.rs, once we figure out how to use macros from there.
|
||||
macro_rules! impl_pubsub_item {
|
||||
($item:ident, $ns:ident) => {
|
||||
impl ::std::convert::TryFrom<crate::Element> for $item {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(elem: crate::Element) -> Result<$item, Error> {
|
||||
check_self!(elem, "item", $ns);
|
||||
check_no_unknown_attributes!(elem, "item", ["id", "publisher"]);
|
||||
let mut payloads = elem.children().cloned().collect::<Vec<_>>();
|
||||
let payload = payloads.pop();
|
||||
if !payloads.is_empty() {
|
||||
return Err(Error::ParseError(
|
||||
"More than a single payload in item element.",
|
||||
));
|
||||
}
|
||||
Ok($item(crate::pubsub::Item {
|
||||
id: get_attr!(elem, "id", Option),
|
||||
publisher: get_attr!(elem, "publisher", Option),
|
||||
payload,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$item> for crate::Element {
|
||||
fn from(item: $item) -> crate::Element {
|
||||
crate::Element::builder("item", ns::$ns)
|
||||
.attr("id", item.0.id)
|
||||
.attr("publisher", item.0.publisher)
|
||||
.append_all(item.0.payload)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::Deref for $item {
|
||||
type Target = crate::pubsub::Item;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::DerefMut for $item {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
15
parsers/src/util/mod.rs
Normal file
15
parsers/src/util/mod.rs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) 2019 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
|
||||
//
|
||||
// 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/.
|
||||
|
||||
/// Error type returned by every parser on failure.
|
||||
pub mod error;
|
||||
|
||||
/// Various helpers.
|
||||
pub(crate) mod helpers;
|
||||
|
||||
/// Helper macros to parse and serialise more easily.
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
Loading…
Reference in a new issue