parsers: use Error type from xso

This is a large change and as such, it needs good motivation. Let me
remind you of the ultimate goal: we want a derive macro which allows us
to FromXml/IntoXml, and that derive macro should be usable from
`xmpp_parsers` and other crates.

For that, any code generated by the derive macro mustn't depend on any
code in the `xmpp_parsers` crate, because you cannot name the crate you
are in portably (`xmpp_parsers::..` wouldn't resolve within
`xmpp_parsers`, and `crate::..` would point at other crates if the macro
was used in other crates).

We also want to interoperate with code already implementing
`TryFrom<Element>` and `Into<Element>` on structs. This ultimately
requires that we have an error type which is shared by the two
implementations and that error type must be declared in the `xso` crate
to be usable by the macros.

Thus, we port the error type over to use the type declared in `xso`.

This changes the structure of the error type greatly; I do not think
that `xso` should have to know about all the different types we are
parsing there and they don't deserve special treatment. Wrapping them in
a `Box<dyn ..>` seems more appropriate.
This commit is contained in:
Jonas Schäfer 2024-06-21 16:27:43 +02:00
commit 6ef8dbefa3
62 changed files with 916 additions and 892 deletions

View file

@ -6,7 +6,13 @@
macro_rules! get_attr {
($elem:ident, $attr:tt, $type:tt) => {
get_attr!($elem, $attr, $type, value, value.parse()?)
get_attr!(
$elem,
$attr,
$type,
value,
value.parse().map_err(xso::error::Error::text_parse_error)?
)
};
($elem:ident, $attr:tt, OptionEmpty, $value:ident, $func:expr) => {
match $elem.attr($attr) {
@ -25,30 +31,27 @@ macro_rules! get_attr {
match $elem.attr($attr) {
Some($value) => $func,
None => {
return Err(crate::util::error::Error::ParseError(concat!(
"Required attribute '",
$attr,
"' missing."
)));
return Err(xso::error::Error::Other(
concat!("Required attribute '", $attr, "' missing.").into(),
)
.into());
}
}
};
($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."
)));
return Err(xso::error::Error::Other(
concat!("Required attribute '", $attr, "' must not be empty.").into(),
)
.into());
}
Some($value) => $func,
None => {
return Err(crate::util::error::Error::ParseError(concat!(
"Required attribute '",
$attr,
"' missing."
)));
return Err(xso::error::Error::Other(
concat!("Required attribute '", $attr, "' missing.").into(),
)
.into());
}
}
};
@ -71,11 +74,11 @@ macro_rules! generate_attribute {
),+
}
impl ::std::str::FromStr for $elem {
type Err = crate::util::error::Error;
fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> {
type Err = xso::error::Error;
fn from_str(s: &str) -> Result<$elem, xso::error::Error> {
Ok(match s {
$($b => $elem::$a),+,
_ => return Err(crate::util::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
_ => return Err(xso::error::Error::Other(concat!("Unknown value for '", $name, "' attribute.")).into()),
})
}
}
@ -104,11 +107,11 @@ macro_rules! generate_attribute {
),+
}
impl ::std::str::FromStr for $elem {
type Err = crate::util::error::Error;
fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> {
type Err = xso::error::Error;
fn from_str(s: &str) -> Result<$elem, xso::error::Error> {
Ok(match s {
$($b => $elem::$a),+,
_ => return Err(crate::util::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
_ => return Err(xso::error::Error::Other(concat!("Unknown value for '", $name, "' attribute.")).into()),
})
}
}
@ -137,11 +140,11 @@ macro_rules! generate_attribute {
None,
}
impl ::std::str::FromStr for $elem {
type Err = crate::util::error::Error;
fn from_str(s: &str) -> Result<Self, crate::util::error::Error> {
type Err = xso::error::Error;
fn from_str(s: &str) -> Result<Self, xso::error::Error> {
Ok(match s {
$value => $elem::$symbol,
_ => return Err(crate::util::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
_ => return Err(xso::error::Error::Other(concat!("Unknown value for '", $name, "' attribute."))),
})
}
}
@ -169,12 +172,12 @@ macro_rules! generate_attribute {
False,
}
impl ::std::str::FromStr for $elem {
type Err = crate::util::error::Error;
fn from_str(s: &str) -> Result<Self, crate::util::error::Error> {
type Err = xso::error::Error;
fn from_str(s: &str) -> Result<Self, xso::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."))),
_ => return Err(xso::error::Error::Other(concat!("Unknown value for '", $name, "' attribute."))),
})
}
}
@ -197,9 +200,9 @@ macro_rules! generate_attribute {
#[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)?))
type Err = xso::error::Error;
fn from_str(s: &str) -> Result<Self, xso::error::Error> {
Ok($elem($type::from_str(s).map_err(xso::error::Error::text_parse_error)?))
}
}
impl ::minidom::IntoAttributeValue for $elem {
@ -229,14 +232,14 @@ macro_rules! generate_element_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> {
type Error = xso::error::FromElementError;
fn try_from(elem: crate::Element) -> Result<$elem, xso::error::FromElementError> {
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."))),
_ => return Err(xso::error::Error::Other(concat!("This is not a ", $name, " element.")).into()),
})
}
}
@ -265,14 +268,14 @@ macro_rules! generate_attribute_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> {
type Error = xso::error::FromElementError;
fn try_from(elem: crate::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(crate::util::error::Error::ParseError(concat!("Invalid ", $name, " ", $attr, " value."))),
_ => return Err(xso::error::Error::Other(concat!("Invalid ", $name, " ", $attr, " value.")).into()),
})
}
}
@ -294,11 +297,7 @@ macro_rules! check_self {
};
($elem:ident, $name:tt, $ns:ident, $pretty_name:tt) => {
if !$elem.is($name, crate::ns::$ns) {
return Err(crate::util::error::Error::TypeMismatch(
$name,
crate::ns::$ns,
$elem,
));
return Err(xso::error::FromElementError::Mismatch($elem));
}
};
}
@ -309,11 +308,10 @@ macro_rules! check_child {
};
($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."
)));
return Err(xso::error::Error::Other(
concat!("This is not a ", $pretty_name, " element.").into(),
)
.into());
}
};
}
@ -321,11 +319,10 @@ macro_rules! check_child {
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."
)));
return Err(xso::error::Error::Other(
concat!("This is not a ", $name, " element.").into(),
)
.into());
}
};
}
@ -334,11 +331,10 @@ 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."
)));
return Err(xso::error::Error::Other(
concat!("Unknown child in ", $name, " element.").into(),
)
.into());
}
};
}
@ -347,11 +343,10 @@ 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."
)));
return Err(xso::error::Error::Other(
concat!("Unknown attribute in ", $name, " element.").into(),
)
.into());
}
};
}
@ -365,7 +360,7 @@ macro_rules! check_no_unknown_attributes {
continue;
}
)*
return Err(crate::util::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element.")));
return Err(xso::error::Error::Other(concat!("Unknown attribute in ", $name, " element.")).into());
}
);
}
@ -377,9 +372,9 @@ macro_rules! generate_empty_element {
pub struct $elem;
impl ::std::convert::TryFrom<crate::Element> for $elem {
type Error = crate::util::error::Error;
type Error = xso::error::FromElementError;
fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> {
fn try_from(elem: crate::Element) -> Result<$elem, xso::error::FromElementError> {
check_self!(elem, $name, $ns);
check_no_children!(elem, $name);
check_no_attributes!(elem, $name);
@ -402,8 +397,8 @@ macro_rules! generate_id {
#[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> {
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)))
}
@ -420,8 +415,8 @@ 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> {
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)))
}
@ -432,13 +427,13 @@ macro_rules! generate_elem_id {
#[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> {
type Error = xso::error::FromElementError;
fn try_from(elem: crate::Element) -> Result<$elem, xso::error::FromElementError> {
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()?))
Ok($elem(elem.text().parse().map_err(xso::error::Error::text_parse_error)?))
}
}
impl From<$elem> for crate::Element {
@ -507,7 +502,7 @@ macro_rules! do_parse {
Ok($elem.text())
};
($elem:ident, $constructor:ident) => {
$constructor::try_from($elem)
$constructor::try_from($elem).map_err(xso::error::Error::from)
};
}
@ -520,13 +515,16 @@ macro_rules! do_parse_elem {
};
($temp:ident: Option = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => {
if $temp.is_some() {
Err(crate::util::error::Error::ParseError(concat!(
"Element ",
$parent_name,
" must not have more than one ",
$name,
" child."
)))
Err(xso::error::Error::Other(
concat!(
"Element ",
$parent_name,
" must not have more than one ",
$name,
" child."
)
.into(),
))
} else {
match do_parse!($elem, $constructor) {
Ok(v) => {
@ -539,13 +537,16 @@ macro_rules! do_parse_elem {
};
($temp:ident: Required = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => {
if $temp.is_some() {
Err(crate::util::error::Error::ParseError(concat!(
"Element ",
$parent_name,
" must not have more than one ",
$name,
" child."
)))
Err(xso::error::Error::Other(
concat!(
"Element ",
$parent_name,
" must not have more than one ",
$name,
" child."
)
.into(),
))
} else {
match do_parse!($elem, $constructor) {
Ok(v) => {
@ -558,13 +559,16 @@ macro_rules! do_parse_elem {
};
($temp:ident: Present = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => {
if $temp {
Err(crate::util::error::Error::ParseError(concat!(
"Element ",
$parent_name,
" must not have more than one ",
$name,
" child."
)))
Err(xso::error::Error::Other(
concat!(
"Element ",
$parent_name,
" must not have more than one ",
$name,
" child."
)
.into(),
))
} else {
$temp = true;
Ok(())
@ -580,13 +584,9 @@ macro_rules! finish_parse_elem {
$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.ok_or(xso::error::Error::Other(
concat!("Missing child ", $name, " in ", $parent_name, " element.").into(),
))?
};
($temp:ident: Present = $name:tt, $parent_name:tt) => {
$temp
@ -694,9 +694,9 @@ macro_rules! generate_element {
}
impl ::std::convert::TryFrom<crate::Element> for $elem {
type Error = crate::util::error::Error;
type Error = xso::error::FromElementError;
fn try_from(mut elem: crate::Element) -> Result<$elem, crate::util::error::Error> {
fn try_from(mut elem: crate::Element) -> Result<$elem, xso::error::FromElementError> {
check_self!(elem, $name, $ns);
check_no_unknown_attributes!(elem, $name, [$($attr_name),*]);
$(
@ -726,14 +726,14 @@ macro_rules! generate_element {
let residual = if generate_child_test!(residual, $child_name, $child_ns) {
match do_parse_elem!($child_ident: $coucou = $child_constructor => residual, $child_name, $name) {
Ok(()) => continue,
Err(other) => return Err(other),
Err(other) => return Err(other.into()),
}
} else {
residual
};
)*
let _ = residual;
return Err(crate::util::error::Error::ParseError(concat!("Unknown child in ", $name, " element.")));
return Err(xso::error::Error::Other(concat!("Unknown child in ", $name, " element.")).into());
}
Ok($elem {
$(
@ -787,17 +787,15 @@ macro_rules! assert_size (
macro_rules! impl_pubsub_item {
($item:ident, $ns:ident) => {
impl ::std::convert::TryFrom<crate::Element> for $item {
type Error = Error;
type Error = FromElementError;
fn try_from(mut elem: crate::Element) -> Result<$item, Error> {
fn try_from(mut elem: crate::Element) -> Result<$item, FromElementError> {
check_self!(elem, "item", $ns);
check_no_unknown_attributes!(elem, "item", ["id", "publisher"]);
let mut payloads = elem.take_contents_as_children().collect::<Vec<_>>();
let payload = payloads.pop();
if !payloads.is_empty() {
return Err(Error::ParseError(
"More than a single payload in item element.",
));
return Err(Error::Other("More than a single payload in item element.").into());
}
Ok($item(crate::pubsub::Item {
id: get_attr!(elem, "id", Option),