Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework errors #675

Merged
merged 24 commits into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
e0adb66
Normalize failed text of all checks for expected errors
Mingun Oct 20, 2023
da2823d
cargo fmt
Mingun Oct 21, 2023
d9520c4
Remove `DeError::UnexpectedEnd`, because this error could be returned…
Mingun Oct 7, 2023
77e5a2c
Remove `DeError::ExpectedStart`, call `Visitor::visit_borrowed_str` o…
Mingun Oct 7, 2023
a5490f2
Do not return `DeError::Unsupported` in `SimpleTypeDeserializer`, cal…
Mingun Oct 20, 2023
f845369
Do not return `DeError::Unsupported` in attempt to deserialize byte data
Mingun Oct 20, 2023
77a2fad
Do not return `DeError::Unsupported` in attempt to deserialize newtyp…
Mingun Oct 20, 2023
29ca886
Replace `Error::UnexpectedEof` and `Error::UnexpectedBang` by `Error:…
Mingun Sep 28, 2023
0e5188d
Replace `Error::UnexpectedEof` by `Error::IllFormed(MissedEnd)` in re…
Mingun Sep 27, 2023
f7ee91f
Do not convert `DeError::InvalidXml(Error::IllFormed(MissedEnd))` to …
Mingun Oct 7, 2023
a91843e
Replace `DeError::UnexpectedEof` by `Error::IllFormed(MissedEnd)` whe…
Mingun Oct 28, 2023
7d20324
Replace `DeError::UnexpectedEof` by `Error::IllFormed(MissedEnd)` in …
Mingun Oct 21, 2023
4972d4f
Replace `DeError::UnexpectedEof` by `Error::IllFormed(MissedEnd)` and…
Mingun Oct 21, 2023
cee2725
`DeEvent::Eof` is impossible in `MapValueDeserializer::variant_seed`,…
Mingun Oct 21, 2023
d5489df
Remove unnecessary allocation
Mingun Oct 8, 2023
00de446
Report trimmed found name in EndEventMismatch error when end tag has …
Mingun Sep 28, 2023
ff28390
Report EndEventMismatch error at start of the end tag at `<` character
Mingun Sep 28, 2023
73fcf23
Inline mismatch_err closure because in the next commit it will genera…
Mingun Sep 28, 2023
6670800
Replace `Error::EndEventMismatch` by `Error::IllFormed(UnmatchedEnd)`…
Mingun Sep 28, 2023
4d887a2
Replace `Error::EndEventMismatch` by `Error::IllFormed(MismatchedEnd)`
Mingun Sep 28, 2023
a4febad
`IllFormed(UnmatchedEnd)` should be reported always because lone clos…
Mingun Sep 29, 2023
596edd6
Replace `Error::UnexpectedToken` by `Error::IllFormed(DoubleHyphenInC…
Mingun Oct 28, 2023
53c6b4e
Remove not used `Error::TextNotFound`
Mingun Oct 28, 2023
a049cd2
Better document `Error::XmlDeclWithoutVersion` and `Error::EmptyDocType`
Mingun Oct 28, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ include = ["src/*", "LICENSE-MIT.md", "README.md"]
[dependencies]
document-features = { version = "0.2", optional = true }
encoding_rs = { version = "0.8", optional = true }
serde = { version = ">=1.0.100", optional = true }
serde = { version = ">=1.0.139", optional = true }
tokio = { version = "1.10", optional = true, default-features = false, features = ["io-util"] }
memchr = "2.1"
arbitrary = { version = "1", features = ["derive"], optional = true }
Expand Down
12 changes: 12 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@

### Misc Changes

- [#675]: Minimum supported version of serde raised to 1.0.139
- [#675]: Rework the `quick_xml::Error` type to provide more accurate information:
- `Error::EndEventMismatch` replaced by `IllFormedError::MismatchedEnd` in some cases
- `Error::EndEventMismatch` replaced by `IllFormedError::UnmatchedEnd` in some cases
- `Error::TextNotFound` was removed because not used
- `Error::UnexpectedBang` replaced by `SyntaxError`
- `Error::UnexpectedEof` replaced by `SyntaxError` in some cases
- `Error::UnexpectedEof` replaced by `IllFormedError` in some cases
- `Error::UnexpectedToken` replaced by `IllFormedError::DoubleHyphenInComment`

[#675]: https://github.com/tafia/quick-xml/pull/675


## 0.31.0 -- 2023-10-22

Expand Down
29 changes: 25 additions & 4 deletions examples/read_nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Note: for this specific data set using serde feature would simplify
// this simple data is purely to make it easier to understand the code

use quick_xml::events::attributes::AttrError;
use quick_xml::events::{BytesStart, Event};
use quick_xml::name::QName;
use quick_xml::reader::Reader;
Expand Down Expand Up @@ -29,6 +30,26 @@ const XML: &str = r#"
</Localization>
"#;

#[derive(Debug)]
enum AppError {
/// XML parsing error
Xml(quick_xml::Error),
/// The `Translation/Text` node is missed
NoText(String),
}

impl From<quick_xml::Error> for AppError {
fn from(error: quick_xml::Error) -> Self {
Self::Xml(error)
}
}

impl From<AttrError> for AppError {
fn from(error: AttrError) -> Self {
Self::Xml(quick_xml::Error::InvalidAttr(error))
}
}

#[derive(Debug)]
struct Translation {
tag: String,
Expand All @@ -40,7 +61,7 @@ impl Translation {
fn new_from_element(
reader: &mut Reader<&[u8]>,
element: BytesStart,
) -> Result<Translation, quick_xml::Error> {
) -> Result<Translation, AppError> {
let mut tag = Cow::Borrowed("");
let mut lang = Cow::Borrowed("");

Expand Down Expand Up @@ -68,16 +89,16 @@ impl Translation {
} else {
dbg!("Expected Event::Start for Text, got: {:?}", &event);
let name_string = reader.decoder().decode(name.as_ref())?;
Err(quick_xml::Error::UnexpectedToken(name_string.into()))
Err(AppError::NoText(name_string.into()))
}
} else {
let event_string = format!("{:?}", event);
Err(quick_xml::Error::UnexpectedToken(event_string))
Err(AppError::NoText(event_string))
}
}
}

fn main() -> Result<(), quick_xml::Error> {
fn main() -> Result<(), AppError> {
// In a real-world use case, Settings would likely be a struct
// HashMap here is just to make the sample code short
let mut settings: HashMap<String, String>;
Expand Down
61 changes: 8 additions & 53 deletions src/de/key.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use crate::de::simple_type::UnitOnly;
use crate::de::str2bool;
use crate::encoding::Decoder;
use crate::errors::serialize::DeError;
use crate::name::QName;
use crate::utils::CowRef;
use serde::de::{DeserializeSeed, Deserializer, EnumAccess, VariantAccess, Visitor};
use serde::de::{DeserializeSeed, Deserializer, EnumAccess, Visitor};
use serde::{forward_to_deserialize_any, serde_if_integer128};
use std::borrow::Cow;

Expand Down Expand Up @@ -240,60 +241,14 @@ impl<'de, 'd> Deserializer<'de> for QNameDeserializer<'de, 'd> {

impl<'de, 'd> EnumAccess<'de> for QNameDeserializer<'de, 'd> {
type Error = DeError;
type Variant = QNameUnitOnly;
type Variant = UnitOnly;

fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error>
where
V: DeserializeSeed<'de>,
{
let name = seed.deserialize(self)?;
Ok((name, QNameUnitOnly))
}
}

////////////////////////////////////////////////////////////////////////////////////////////////////

/// Deserializer of variant data, that supports only unit variants.
/// Attempt to deserialize newtype, tuple or struct variant will return a
/// [`DeError::Unsupported`] error.
pub struct QNameUnitOnly;
impl<'de> VariantAccess<'de> for QNameUnitOnly {
type Error = DeError;

#[inline]
fn unit_variant(self) -> Result<(), DeError> {
Ok(())
}

fn newtype_variant_seed<T>(self, _seed: T) -> Result<T::Value, DeError>
where
T: DeserializeSeed<'de>,
{
Err(DeError::Unsupported(
"enum newtype variants are not supported as an XML names".into(),
))
}

fn tuple_variant<V>(self, _len: usize, _visitor: V) -> Result<V::Value, DeError>
where
V: Visitor<'de>,
{
Err(DeError::Unsupported(
"enum tuple variants are not supported as an XML names".into(),
))
}

fn struct_variant<V>(
self,
_fields: &'static [&'static str],
_visitor: V,
) -> Result<V::Value, DeError>
where
V: Visitor<'de>,
{
Err(DeError::Unsupported(
"enum struct variants are not supported as an XML names".into(),
))
Ok((name, UnitOnly))
}
}

Expand Down Expand Up @@ -405,7 +360,7 @@ mod tests {
match err {
DeError::$kind(e) => assert_eq!(e, $reason),
_ => panic!(
"Expected `{}({})`, found `{:?}`",
"Expected `Err({}({}))`, but got `{:?}`",
stringify!($kind),
$reason,
err
Expand Down Expand Up @@ -473,11 +428,11 @@ mod tests {
deserialized_to!(enum_unit: Enum = "Unit" => Enum::Unit);
deserialized_to!(enum_unit_for_attr: Enum = "@Attr" => Enum::Attr);
err!(enum_newtype: Enum = "Newtype"
=> Unsupported("enum newtype variants are not supported as an XML names"));
=> Custom("invalid type: unit value, expected a string"));
err!(enum_tuple: Enum = "Tuple"
=> Unsupported("enum tuple variants are not supported as an XML names"));
=> Custom("invalid type: unit value, expected tuple variant Enum::Tuple"));
err!(enum_struct: Enum = "Struct"
=> Unsupported("enum struct variants are not supported as an XML names"));
=> Custom("invalid type: unit value, expected struct variant Enum::Struct"));

// Field identifiers cannot be serialized, and IgnoredAny represented _something_
// which is not concrete
Expand Down
23 changes: 13 additions & 10 deletions src/de/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::{
de::{str2bool, DeEvent, Deserializer, XmlRead, TEXT_KEY, VALUE_KEY},
encoding::Decoder,
errors::serialize::DeError,
errors::Error,
events::attributes::IterState,
events::BytesStart,
name::QName,
Expand Down Expand Up @@ -300,7 +301,7 @@ where
}
// We cannot get `Eof` legally, because we always inside of the
// opened tag `self.start`
DeEvent::Eof => Err(DeError::UnexpectedEof),
DeEvent::Eof => Err(Error::missed_end(self.start.name(), decoder).into()),
}
}
}
Expand Down Expand Up @@ -616,9 +617,9 @@ where
if self.fixed_name {
match self.map.de.next()? {
// Handles <field>UnitEnumVariant</field>
DeEvent::Start(_) => {
DeEvent::Start(e) => {
// skip <field>, read text after it and ensure that it is ended by </field>
let text = self.map.de.read_text()?;
let text = self.map.de.read_text(e.name())?;
if text.is_empty() {
// Map empty text (<field/>) to a special `$text` variant
visitor.visit_enum(SimpleTypeDeserializer::from_text(TEXT_KEY.into()))
Expand Down Expand Up @@ -669,8 +670,8 @@ where
seed.deserialize(BorrowedStrDeserializer::<DeError>::new(TEXT_KEY))?,
true,
),
DeEvent::End(e) => return Err(DeError::UnexpectedEnd(e.name().into_inner().to_vec())),
DeEvent::Eof => return Err(DeError::UnexpectedEof),
// SAFETY: we use that deserializer only when we peeked `Start` or `Text` event
_ => unreachable!(),
};
Ok((
name,
Expand Down Expand Up @@ -927,12 +928,14 @@ where
DeEvent::Start(e) if !self.filter.is_suitable(e, decoder)? => Ok(None),

// Stop iteration after reaching a closing tag
DeEvent::End(e) if e.name() == self.map.start.name() => Ok(None),
// This is a unmatched closing tag, so the XML is invalid
DeEvent::End(e) => Err(DeError::UnexpectedEnd(e.name().as_ref().to_owned())),
// The matching tag name is guaranteed by the reader
DeEvent::End(e) => {
debug_assert_eq!(self.map.start.name(), e.name());
Ok(None)
}
// We cannot get `Eof` legally, because we always inside of the
// opened tag `self.map.start`
DeEvent::Eof => Err(DeError::UnexpectedEof),
DeEvent::Eof => Err(Error::missed_end(self.map.start.name(), decoder).into()),

DeEvent::Text(_) => match self.map.de.next()? {
DeEvent::Text(e) => seed.deserialize(TextDeserializer(e)).map(Some),
Expand Down Expand Up @@ -1018,7 +1021,7 @@ where
/// [`CData`]: crate::events::Event::CData
#[inline]
fn read_string(&mut self) -> Result<Cow<'de, str>, DeError> {
self.de.read_text()
self.de.read_text(self.start.name())
}
}

Expand Down
Loading
Loading