From e0adb6615d4371e52fbbc4132b446ef288bdd3fa Mon Sep 17 00:00:00 2001 From: Mingun Date: Sat, 21 Oct 2023 03:09:05 +0500 Subject: [PATCH 01/24] Normalize failed text of all checks for expected errors --- src/de/key.rs | 2 +- src/de/mod.rs | 28 ++++++------- src/de/simple_type.rs | 4 +- src/name.rs | 14 +++---- src/reader/mod.rs | 85 ++++++++++++++++---------------------- src/se/content.rs | 4 +- src/se/element.rs | 4 +- src/se/key.rs | 2 +- src/se/simple_type.rs | 4 +- tests/issues.rs | 6 +-- tests/serde-de-enum.rs | 6 +-- tests/serde-de-seq.rs | 92 +++++++++++++++++++++--------------------- tests/serde-de.rs | 84 +++++++++++++++++++------------------- tests/serde-se.rs | 6 +-- 14 files changed, 162 insertions(+), 179 deletions(-) diff --git a/src/de/key.rs b/src/de/key.rs index 2645d906..9ef9d4b3 100644 --- a/src/de/key.rs +++ b/src/de/key.rs @@ -405,7 +405,7 @@ mod tests { match err { DeError::$kind(e) => assert_eq!(e, $reason), _ => panic!( - "Expected `{}({})`, found `{:?}`", + "Expected `Err({}({}))`, but got `{:?}`", stringify!($kind), $reason, err diff --git a/src/de/mod.rs b/src/de/mod.rs index a7190f6e..ac0bc9ef 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -3539,7 +3539,7 @@ mod tests { match List::deserialize(&mut de) { Err(DeError::TooManyEvents(count)) => assert_eq!(count.get(), 3), - e => panic!("Expected `Err(TooManyEvents(3))`, but found {:?}", e), + e => panic!("Expected `Err(TooManyEvents(3))`, but got `{:?}`", e), } } @@ -3605,8 +3605,8 @@ mod tests { assert_eq!(de.peek().unwrap(), &Start(BytesStart::new("tag"))); match de.read_to_end(QName(b"tag")) { - Err(DeError::UnexpectedEof) => (), - x => panic!("Expected `Err(UnexpectedEof)`, but found {:?}", x), + Err(DeError::UnexpectedEof) => {} + x => panic!("Expected `Err(UnexpectedEof)`, but got `{:?}`", x), } assert_eq!(de.next().unwrap(), Eof); } @@ -3619,8 +3619,8 @@ mod tests { assert_eq!(de.peek().unwrap(), &Text("".into())); match de.read_to_end(QName(b"tag")) { - Err(DeError::UnexpectedEof) => (), - x => panic!("Expected `Err(UnexpectedEof)`, but found {:?}", x), + Err(DeError::UnexpectedEof) => {} + x => panic!("Expected `Err(UnexpectedEof)`, but got `{:?}`", x), } assert_eq!(de.next().unwrap(), Eof); } @@ -3713,7 +3713,7 @@ mod tests { assert_eq!(found, "root"); } x => panic!( - r#"Expected `Err(InvalidXml(EndEventMismatch("", "root")))`, but found {:?}"#, + "Expected `Err(InvalidXml(EndEventMismatch {{ expected = '', found = 'root' }}))`, but got `{:?}`", x ), } @@ -3727,7 +3727,7 @@ mod tests { assert_eq!(found, "other"); } x => panic!( - r#"Expected `Err(InvalidXml(EndEventMismatch("root", "other")))`, but found {:?}"#, + "Expected `Err(InvalidXml(EndEventMismatch {{ expected = 'root', found = 'other' }}))`, but got `{:?}`", x ), } @@ -4055,7 +4055,7 @@ mod tests { assert_eq!(expected, ""); assert_eq!(found, "tag2"); } - x => panic!("Expected `InvalidXml(EndEventMismatch {{ expected = '', found = 'tag2' }})`, but got {:?}", x), + x => panic!("Expected `Err(InvalidXml(EndEventMismatch {{ expected = '', found = 'tag2' }}))`, but got `{:?}`", x), } assert_eq!(de.next().unwrap(), DeEvent::Eof); } @@ -4196,7 +4196,7 @@ mod tests { assert_eq!(expected, ""); assert_eq!(found, "tag"); } - x => panic!("Expected `InvalidXml(EndEventMismatch {{ expected = '', found = 'tag' }})`, but got {:?}", x), + x => panic!("Expected `Err(InvalidXml(EndEventMismatch {{ expected = '', found = 'tag' }}))`, but got `{:?}`", x), } assert_eq!(de.next().unwrap(), DeEvent::Eof); } @@ -4273,7 +4273,7 @@ mod tests { assert_eq!(expected, ""); assert_eq!(found, "tag"); } - x => panic!("Expected `InvalidXml(EndEventMismatch {{ expected = '', found = 'tag' }})`, but got {:?}", x), + x => panic!("Expected `Err(InvalidXml(EndEventMismatch {{ expected = '', found = 'tag' }}))`, but got `{:?}`", x), } assert_eq!(de.next().unwrap(), DeEvent::Eof); } @@ -4303,7 +4303,7 @@ mod tests { assert_eq!(expected, ""); assert_eq!(found, "tag"); } - x => panic!("Expected `InvalidXml(EndEventMismatch {{ expected = '', found = 'tag' }})`, but got {:?}", x), + x => panic!("Expected `Err(InvalidXml(EndEventMismatch {{ expected = '', found = 'tag' }}))`, but got `{:?}`", x), } assert_eq!(de.next().unwrap(), DeEvent::Eof); } @@ -4407,7 +4407,7 @@ mod tests { assert_eq!(expected, ""); assert_eq!(found, "tag"); } - x => panic!("Expected `InvalidXml(EndEventMismatch {{ expected = '', found = 'tag' }})`, but got {:?}", x), + x => panic!("Expected `Err(InvalidXml(EndEventMismatch {{ expected = '', found = 'tag' }}))`, but got `{:?}`", x), } assert_eq!(de.next().unwrap(), DeEvent::Eof); } @@ -4435,7 +4435,7 @@ mod tests { assert_eq!(expected, ""); assert_eq!(found, "tag"); } - x => panic!("Expected `InvalidXml(EndEventMismatch {{ expected = '', found = 'tag' }})`, but got {:?}", x), + x => panic!("Expected `Err(InvalidXml(EndEventMismatch {{ expected = '', found = 'tag' }}))`, but got `{:?}`", x), } assert_eq!(de.next().unwrap(), DeEvent::Eof); } @@ -4483,7 +4483,7 @@ mod tests { assert_eq!(expected, ""); assert_eq!(found, "tag"); } - x => panic!("Expected `InvalidXml(EndEventMismatch {{ expected = '', found = 'tag' }})`, but got {:?}", x), + x => panic!("Expected `Err(InvalidXml(EndEventMismatch {{ expected = '', found = 'tag' }}))`, but got `{:?}`", x), } assert_eq!(de.next().unwrap(), DeEvent::Eof); } diff --git a/src/de/simple_type.rs b/src/de/simple_type.rs index aa3be1e5..52fb7410 100644 --- a/src/de/simple_type.rs +++ b/src/de/simple_type.rs @@ -886,7 +886,7 @@ mod tests { match err { DeError::$kind(e) => assert_eq!(e, $reason), _ => panic!( - "Expected `{}({})`, found `{:?}`", + "Expected `Err({}({}))`, but got `{:?}`", stringify!($kind), $reason, err @@ -1004,7 +1004,7 @@ mod tests { match err { DeError::$kind(e) => assert_eq!(e, $reason), _ => panic!( - "Expected `{}({})`, found `{:?}`", + "Expected `Err({}({}))`, but got `{:?}`", stringify!($kind), $reason, err diff --git a/src/name.rs b/src/name.rs index a6128686..7ac207c1 100644 --- a/src/name.rs +++ b/src/name.rs @@ -931,7 +931,7 @@ mod namespaces { assert_eq!(namespace, b"not_correct_namespace"); } x => panic!( - "Expected `Error::ReservedNamespaceError`, but found {:?}", + "Expected `Err(InvalidPrefixBind {{ .. }})`, but got `{:?}`", x ), } @@ -949,7 +949,7 @@ mod namespaces { assert_eq!(namespace, b""); } x => panic!( - "Expected `Error::ReservedNamespaceError`, but found {:?}", + "Expected `Err(InvalidPrefixBind {{ .. }})`, but got `{:?}`", x ), } @@ -970,7 +970,7 @@ mod namespaces { assert_eq!(namespace, b"http://www.w3.org/XML/1998/namespace"); } x => panic!( - "Expected `Error::ReservedNamespaceError`, but found {:?}", + "Expected `Err(InvalidPrefixBind {{ .. }})`, but got `{:?}`", x ), } @@ -1016,7 +1016,7 @@ mod namespaces { assert_eq!(namespace, b"http://www.w3.org/2000/xmlns/"); } x => panic!( - "Expected `Error::ReservedNamespaceError`, but found {:?}", + "Expected `Err(InvalidPrefixBind {{ .. }})`, but got `{:?}`", x ), } @@ -1037,7 +1037,7 @@ mod namespaces { assert_eq!(namespace, b"not_correct_namespace"); } x => panic!( - "Expected `Error::ReservedNamespaceError`, but found {:?}", + "Expected `Err(InvalidPrefixBind {{ .. }})`, but got `{:?}`", x ), } @@ -1055,7 +1055,7 @@ mod namespaces { assert_eq!(namespace, b""); } x => panic!( - "Expected `Error::ReservedNamespaceError`, but found {:?}", + "Expected `Err(InvalidPrefixBind {{ .. }})`, but got `{:?}`", x ), } @@ -1076,7 +1076,7 @@ mod namespaces { assert_eq!(namespace, b"http://www.w3.org/2000/xmlns/"); } x => panic!( - "Expected `Error::ReservedNamespaceError`, but found {:?}", + "Expected `Err(InvalidPrefixBind {{ .. }})`, but got `{:?}`", x ), } diff --git a/src/reader/mod.rs b/src/reader/mod.rs index b49c3650..b63977e0 100644 --- a/src/reader/mod.rs +++ b/src/reader/mod.rs @@ -1074,9 +1074,8 @@ mod test { match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? { Err(Error::UnexpectedEof(s)) if s == "CData" => {} - x => assert!( - false, - r#"Expected `UnexpectedEof("CData")`, but result is: {:?}"#, + x => panic!( + r#"Expected `Err(UnexpectedEof("CData"))`, but got `{:?}`"#, x ), } @@ -1094,9 +1093,8 @@ mod test { match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? { Err(Error::UnexpectedEof(s)) if s == "CData" => {} - x => assert!( - false, - r#"Expected `UnexpectedEof("CData")`, but result is: {:?}"#, + x => panic!( + r#"Expected `Err(UnexpectedEof("CData"))`, but got `{:?}`"#, x ), } @@ -1177,9 +1175,8 @@ mod test { match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? { Err(Error::UnexpectedEof(s)) if s == "Comment" => {} - x => assert!( - false, - r#"Expected `UnexpectedEof("Comment")`, but result is: {:?}"#, + x => panic!( + r#"Expected `Err(UnexpectedEof("Comment"))`, but got `{:?}`"#, x ), } @@ -1195,9 +1192,8 @@ mod test { match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? { Err(Error::UnexpectedEof(s)) if s == "Comment" => {} - x => assert!( - false, - r#"Expected `UnexpectedEof("Comment")`, but result is: {:?}"#, + x => panic!( + r#"Expected `Err(UnexpectedEof("Comment"))`, but got `{:?}`"#, x ), } @@ -1213,9 +1209,8 @@ mod test { match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? { Err(Error::UnexpectedEof(s)) if s == "Comment" => {} - x => assert!( - false, - r#"Expected `UnexpectedEof("Comment")`, but result is: {:?}"#, + x => panic!( + r#"Expected `Err(UnexpectedEof("Comment"))`, but got `{:?}`"#, x ), } @@ -1231,9 +1226,8 @@ mod test { match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? { Err(Error::UnexpectedEof(s)) if s == "Comment" => {} - x => assert!( - false, - r#"Expected `UnexpectedEof("Comment")`, but result is: {:?}"#, + x => panic!( + r#"Expected `Err(UnexpectedEof("Comment"))`, but got `{:?}`"#, x ), } @@ -1249,9 +1243,8 @@ mod test { match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? { Err(Error::UnexpectedEof(s)) if s == "Comment" => {} - x => assert!( - false, - r#"Expected `UnexpectedEof("Comment")`, but result is: {:?}"#, + x => panic!( + r#"Expected `Err(UnexpectedEof("Comment"))`, but got `{:?}`"#, x ), } @@ -1315,9 +1308,8 @@ mod test { match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? { Err(Error::UnexpectedEof(s)) if s == "DOCTYPE" => {} - x => assert!( - false, - r#"Expected `UnexpectedEof("DOCTYPE")`, but result is: {:?}"#, + x => panic!( + r#"Expected `Err(UnexpectedEof("DOCTYPE"))`, but got `{:?}`"#, x ), } @@ -1333,9 +1325,8 @@ mod test { match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? { Err(Error::UnexpectedEof(s)) if s == "DOCTYPE" => {} - x => assert!( - false, - r#"Expected `UnexpectedEof("DOCTYPE")`, but result is: {:?}"#, + x => panic!( + r#"Expected `Err(UnexpectedEof("DOCTYPE"))`, but got `{:?}`"#, x ), } @@ -1369,9 +1360,8 @@ mod test { match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? { Err(Error::UnexpectedEof(s)) if s == "DOCTYPE" => {} - x => assert!( - false, - r#"Expected `UnexpectedEof("DOCTYPE")`, but result is: {:?}"#, + x => panic!( + r#"Expected `Err(UnexpectedEof("DOCTYPE"))`, but got `{:?}`"#, x ), } @@ -1395,9 +1385,8 @@ mod test { match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? { Err(Error::UnexpectedEof(s)) if s == "DOCTYPE" => {} - x => assert!( - false, - r#"Expected `UnexpectedEof("DOCTYPE")`, but result is: {:?}"#, + x => panic!( + r#"Expected `Err(UnexpectedEof("DOCTYPE"))`, but got `{:?}`"#, x ), } @@ -1413,9 +1402,8 @@ mod test { match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? { Err(Error::UnexpectedEof(s)) if s == "DOCTYPE" => {} - x => assert!( - false, - r#"Expected `UnexpectedEof("DOCTYPE")`, but result is: {:?}"#, + x => panic!( + r#"Expected `Err(UnexpectedEof("DOCTYPE"))`, but got `{:?}`"#, x ), } @@ -1449,9 +1437,8 @@ mod test { match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? { Err(Error::UnexpectedEof(s)) if s == "DOCTYPE" => {} - x => assert!( - false, - r#"Expected `UnexpectedEof("DOCTYPE")`, but result is: {:?}"#, + x => panic!( + r#"Expected `Err(UnexpectedEof("DOCTYPE"))`, but got `{:?}`"#, x ), } @@ -1644,9 +1631,8 @@ mod test { match reader.$read_until_close($buf) $(.$await)? { Err(Error::UnexpectedEof(s)) if s == "CData" => {} - x => assert!( - false, - r#"Expected `UnexpectedEof("CData")`, but result is: {:?}"#, + x => panic!( + r#"Expected `Err(UnexpectedEof("CData"))`, but got `{:?}`"#, x ), } @@ -1658,9 +1644,8 @@ mod test { match reader.$read_until_close($buf) $(.$await)? { Err(Error::UnexpectedEof(s)) if s == "Comment" => {} - x => assert!( - false, - r#"Expected `UnexpectedEof("Comment")`, but result is: {:?}"#, + x => panic!( + r#"Expected `Err(UnexpectedEof("Comment"))`, but got `{:?}`"#, x ), } @@ -1672,9 +1657,8 @@ mod test { match reader.$read_until_close($buf) $(.$await)? { Err(Error::UnexpectedEof(s)) if s == "DOCTYPE" => {} - x => assert!( - false, - r#"Expected `UnexpectedEof("DOCTYPE")`, but result is: {:?}"#, + x => panic!( + r#"Expected `Err(UnexpectedEof("DOCTYPE"))`, but got `{:?}`"#, x ), } @@ -1686,9 +1670,8 @@ mod test { match reader.$read_until_close($buf) $(.$await)? { Err(Error::UnexpectedEof(s)) if s == "DOCTYPE" => {} - x => assert!( - false, - r#"Expected `UnexpectedEof("DOCTYPE")`, but result is: {:?}"#, + x => panic!( + r#"Expected `Err(UnexpectedEof("DOCTYPE"))`, but got `{:?}`"#, x ), } diff --git a/src/se/content.rs b/src/se/content.rs index 984689f0..777338de 100644 --- a/src/se/content.rs +++ b/src/se/content.rs @@ -575,7 +575,7 @@ pub(super) mod tests { match $data.serialize(ser).unwrap_err() { DeError::$kind(e) => assert_eq!(e, $reason), e => panic!( - "Expected `{}({})`, found `{:?}`", + "Expected `Err({}({}))`, but got `{:?}`", stringify!($kind), $reason, e @@ -1015,7 +1015,7 @@ pub(super) mod tests { match $data.serialize(ser).unwrap_err() { DeError::$kind(e) => assert_eq!(e, $reason), e => panic!( - "Expected `{}({})`, found `{:?}`", + "Expected `Err({}({}))`, but got `{:?}`", stringify!($kind), $reason, e diff --git a/src/se/element.rs b/src/se/element.rs index afad2a97..3d6056ea 100644 --- a/src/se/element.rs +++ b/src/se/element.rs @@ -645,7 +645,7 @@ mod tests { match $data.serialize(ser).unwrap_err() { DeError::$kind(e) => assert_eq!(e, $reason), e => panic!( - "Expected `{}({})`, found `{:?}`", + "Expected `Err({}({}))`, but got `{:?}`", stringify!($kind), $reason, e @@ -1348,7 +1348,7 @@ mod tests { match $data.serialize(ser).unwrap_err() { DeError::$kind(e) => assert_eq!(e, $reason), e => panic!( - "Expected `{}({})`, found `{:?}`", + "Expected `Err({}({}))`, but got `{:?}`", stringify!($kind), $reason, e diff --git a/src/se/key.rs b/src/se/key.rs index f5f6e047..c260f4e5 100644 --- a/src/se/key.rs +++ b/src/se/key.rs @@ -216,7 +216,7 @@ mod tests { match $data.serialize(ser).unwrap_err() { DeError::$kind(e) => assert_eq!(e, $reason), e => panic!( - "Expected `{}({})`, found `{:?}`", + "Expected `Err({}({}))`, but got `{:?}`", stringify!($kind), $reason, e diff --git a/src/se/simple_type.rs b/src/se/simple_type.rs index e396606d..442af45a 100644 --- a/src/se/simple_type.rs +++ b/src/se/simple_type.rs @@ -956,7 +956,7 @@ mod tests { match $data.serialize(ser).unwrap_err() { DeError::$kind(e) => assert_eq!(e, $reason), e => panic!( - "Expected `{}({})`, found `{:?}`", + "Expected `Err({}({}))`, but got `{:?}`", stringify!($kind), $reason, e @@ -1074,7 +1074,7 @@ mod tests { match $data.serialize(ser).unwrap_err() { DeError::$kind(e) => assert_eq!(e, $reason), e => panic!( - "Expected `{}({})`, found `{:?}`", + "Expected `Err({}({}))`, but got `{:?}`", stringify!($kind), $reason, e diff --git a/tests/issues.rs b/tests/issues.rs index e6d52758..c43f75a8 100644 --- a/tests/issues.rs +++ b/tests/issues.rs @@ -149,7 +149,7 @@ mod issue514 { assert_eq!(found, "other-tag"); } x => panic!( - r#"Expected `Err(EndEventMismatch("some-tag", "other-tag"))`, but found {:?}"#, + "Expected `Err(EndEventMismatch {{ expected = 'some-tag', found = 'other-tag' }})`, but got `{:?}`", x ), } @@ -174,7 +174,7 @@ mod issue604 { match reader.read_event_into(&mut buf) { Err(Error::UnexpectedEof(reason)) => assert_eq!(reason, "Comment"), x => panic!( - r#"Expected `Err(UnexpectedEof("Comment"))`, but found {:?}"#, + r#"Expected `Err(UnexpectedEof("Comment"))`, but got `{:?}`"#, x ), } @@ -193,7 +193,7 @@ mod issue604 { match reader.read_event_into(&mut buf) { Err(Error::UnexpectedEof(reason)) => assert_eq!(reason, "Comment"), x => panic!( - r#"Expected `Err(UnexpectedEof("Comment"))`, but found {:?}"#, + r#"Expected `Err(UnexpectedEof("Comment"))`, but got `{:?}`"#, x ), } diff --git a/tests/serde-de-enum.rs b/tests/serde-de-enum.rs index 351f7cd3..4b66b0cc 100644 --- a/tests/serde-de-enum.rs +++ b/tests/serde-de-enum.rs @@ -331,7 +331,7 @@ mod externally_tagged { fn text() { match from_str::(" text ") { Err(DeError::Unsupported(_)) => {} - x => panic!("Expected `Err(Unsupported(_))`, but found {:?}", x), + x => panic!("Expected `Err(Unsupported(_))`, but got `{:?}`", x), } } @@ -339,7 +339,7 @@ mod externally_tagged { fn cdata() { match from_str::("") { Err(DeError::Unsupported(_)) => {} - x => panic!("Expected `Err(Unsupported(_))`, but found {:?}", x), + x => panic!("Expected `Err(Unsupported(_))`, but got `{:?}`", x), } } @@ -347,7 +347,7 @@ mod externally_tagged { fn mixed() { match from_str::(" te xt ") { Err(DeError::Unsupported(_)) => {} - x => panic!("Expected `Err(Unsupported(_))`, but found {:?}", x), + x => panic!("Expected `Err(Unsupported(_))`, but got `{:?}`", x), } } } diff --git a/tests/serde-de-seq.rs b/tests/serde-de-seq.rs index ced82980..24c50167 100644 --- a/tests/serde-de-seq.rs +++ b/tests/serde-de-seq.rs @@ -236,7 +236,7 @@ mod fixed_name { assert_eq!(e, "invalid length 2, expected an array of length 3") } e => panic!( - r#"Expected `Err(Custom("invalid length 2, expected an array of length 3"))`, but found {:?}"#, + r#"Expected `Err(Custom("invalid length 2, expected an array of length 3"))`, but got `{:?}`"#, e ), } @@ -262,7 +262,7 @@ mod fixed_name { match data { Err(DeError::Custom(e)) => assert_eq!(e, "duplicate field `item`"), e => panic!( - r#"Expected `Err(Custom("duplicate field `item`"))`, but found {:?}"#, + r#"Expected `Err(Custom("duplicate field `item`"))`, but got `{:?}`"#, e ), } @@ -286,7 +286,7 @@ mod fixed_name { match data { Err(DeError::Custom(e)) => assert_eq!(e, "missing field `item`"), e => panic!( - r#"Expected `Err(Custom("missing field `item`"))`, but found {:?}"#, + r#"Expected `Err(Custom("missing field `item`"))`, but got `{:?}`"#, e ), } @@ -352,7 +352,7 @@ mod fixed_name { assert_eq!(e, "invalid length 1, expected an array of length 3") } e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + r#"Expected `Err(Custom("invalid length 1, expected an array of length 3"))`, but got `{:?}`"#, e ), } @@ -386,7 +386,7 @@ mod fixed_name { assert_eq!(e, "invalid length 1, expected an array of length 3") } e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + r#"Expected `Err(Custom("invalid length 1, expected an array of length 3"))`, but got `{:?}`"#, e ), } @@ -460,7 +460,7 @@ mod fixed_name { assert_eq!(e, "invalid length 1, expected an array of length 3") } e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + r#"Expected `Err(Custom("invalid length 1, expected an array of length 3"))`, but got `{:?}`"#, e ), } @@ -495,7 +495,7 @@ mod fixed_name { assert_eq!(e, "invalid length 1, expected an array of length 3") } e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + r#"Expected `Err(Custom("invalid length 1, expected an array of length 3"))`, but got `{:?}`"#, e ), } @@ -569,7 +569,7 @@ mod fixed_name { assert_eq!(e, "invalid length 1, expected an array of length 3") } e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + r#"Expected `Err(Custom("invalid length 1, expected an array of length 3"))`, but got `{:?}`"#, e ), } @@ -604,7 +604,7 @@ mod fixed_name { assert_eq!(e, "invalid length 1, expected an array of length 3") } e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + r#"Expected `Err(Custom("invalid length 1, expected an array of length 3"))`, but got `{:?}`"#, e ), } @@ -663,7 +663,7 @@ mod fixed_name { assert_eq!(e, "invalid length 1, expected an array of length 3") } e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + r#"Expected `Err(Custom("invalid length 1, expected an array of length 3"))`, but got `{:?}`"#, e ), } @@ -699,7 +699,7 @@ mod fixed_name { assert_eq!(e, "invalid length 1, expected an array of length 3") } e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + r#"Expected `Err(Custom("invalid length 1, expected an array of length 3"))`, but got `{:?}`"#, e ), } @@ -1042,7 +1042,7 @@ mod fixed_name { match data { Err(DeError::Custom(e)) => assert_eq!(e, "missing field `item`"), e => panic!( - r#"Expected `Err(Custom("missing field `item`"))`, but found {:?}"#, + r#"Expected `Err(Custom("missing field `item`"))`, but got `{:?}`"#, e ), } @@ -1124,7 +1124,7 @@ mod fixed_name { match data { Err(DeError::Custom(e)) => assert_eq!(e, "duplicate field `item`"), e => panic!( - r#"Expected Err(Custom("duplicate field `item`")), got {:?}"#, + r#"Expected `Err(Custom("duplicate field `item`"))`, but got `{:?}`"#, e ), } @@ -1165,7 +1165,7 @@ mod fixed_name { match data { Err(DeError::Custom(e)) => assert_eq!(e, "duplicate field `outer`"), e => panic!( - r#"Expected Err(Custom("duplicate field `outer`")), got {:?}"#, + r#"Expected `Err(Custom("duplicate field `outer`"))`, but got `{:?}`"#, e ), } @@ -1260,7 +1260,7 @@ mod fixed_name { assert_eq!(e, "duplicate field `item`") } e => panic!( - r#"Expected Err(Custom("duplicate field `item`")), got {:?}"#, + r#"Expected `Err(Custom("duplicate field `item`"))`, but got `{:?}`"#, e ), } @@ -1303,7 +1303,7 @@ mod fixed_name { match data { Err(DeError::Custom(e)) => assert_eq!(e, "duplicate field `outer`"), e => panic!( - r#"Expected Err(Custom("duplicate field `outer`")), got {:?}"#, + r#"Expected `Err(Custom("duplicate field `outer`"))`, but got `{:?}`"#, e ), } @@ -1398,7 +1398,7 @@ mod fixed_name { assert_eq!(e, "duplicate field `item`") } e => panic!( - r#"Expected Err(Custom("duplicate field `item`")), got {:?}"#, + r#"Expected `Err(Custom("duplicate field `item`"))`, but got `{:?}`"#, e ), } @@ -1441,7 +1441,7 @@ mod fixed_name { match data { Err(DeError::Custom(e)) => assert_eq!(e, "duplicate field `outer`"), e => panic!( - r#"Expected Err(Custom("duplicate field `outer`")), got {:?}"#, + r#"Expected `Err(Custom("duplicate field `outer`"))`, but got `{:?}`"#, e ), } @@ -1511,7 +1511,7 @@ mod fixed_name { match data { Err(DeError::Custom(e)) => assert_eq!(e, "duplicate field `item`"), e => panic!( - r#"Expected Err(Custom("duplicate field `item`")), got {:?}"#, + r#"Expected `Err(Custom("duplicate field `item`"))`, but got `{:?}`"#, e ), } @@ -1553,7 +1553,7 @@ mod fixed_name { match data { Err(DeError::Custom(e)) => assert_eq!(e, "duplicate field `outer`"), e => panic!( - r#"Expected Err(Custom("duplicate field `outer`")), got {:?}"#, + r#"Expected `Err(Custom("duplicate field `outer`"))`, but got `{:?}`"#, e ), } @@ -2128,7 +2128,7 @@ mod variable_name { assert_eq!(e, "invalid length 1, expected an array of length 3") } e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + r#"Expected `Err(Custom("invalid length 1, expected an array of length 3"))`, but got `{:?}`"#, e ), } @@ -2174,7 +2174,7 @@ mod variable_name { assert_eq!(e, "invalid length 1, expected an array of length 3") } e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + r#"Expected `Err(Custom("invalid length 1, expected an array of length 3"))`, but got `{:?}`"#, e ), } @@ -2270,7 +2270,7 @@ mod variable_name { assert_eq!(e, "invalid length 1, expected an array of length 3") } e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + r#"Expected `Err(Custom("invalid length 1, expected an array of length 3"))`, but got `{:?}`"#, e ), } @@ -2316,7 +2316,7 @@ mod variable_name { assert_eq!(e, "invalid length 1, expected an array of length 3") } e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + r#"Expected `Err(Custom("invalid length 1, expected an array of length 3"))`, but got `{:?}`"#, e ), } @@ -2435,7 +2435,7 @@ mod variable_name { assert_eq!(e, "invalid length 1, expected an array of length 2") } e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 2")), got {:?}"#, + r#"Expected `Err(Custom("invalid length 1, expected an array of length 2"))`, but got `{:?}`"#, e ), } @@ -2472,7 +2472,7 @@ mod variable_name { assert_eq!(e, "invalid length 1, expected an array of length 3") } e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + r#"Expected `Err(Custom("invalid length 1, expected an array of length 3"))`, but got `{:?}`"#, e ), } @@ -2512,7 +2512,7 @@ mod variable_name { assert_eq!(e, "invalid length 1, expected an array of length 2") } e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 2")), got {:?}"#, + r#"Expected `Err(Custom("invalid length 1, expected an array of length 2"))`, but got `{:?}`"#, e ), } @@ -2552,7 +2552,7 @@ mod variable_name { assert_eq!(e, "invalid length 1, expected an array of length 3") } e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + r#"Expected `Err(Custom("invalid length 1, expected an array of length 3"))`, but got `{:?}`"#, e ), } @@ -2667,7 +2667,7 @@ mod variable_name { assert_eq!(e, "invalid length 1, expected an array of length 2") } e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 2")), got {:?}"#, + r#"Expected `Err(Custom("invalid length 1, expected an array of length 2"))`, but got `{:?}`"#, e ), } @@ -2704,7 +2704,7 @@ mod variable_name { assert_eq!(e, "invalid length 1, expected an array of length 3") } e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + r#"Expected `Err(Custom("invalid length 1, expected an array of length 3"))`, but got `{:?}`"#, e ), } @@ -2744,7 +2744,7 @@ mod variable_name { assert_eq!(e, "invalid length 1, expected an array of length 2") } e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 2")), got {:?}"#, + r#"Expected `Err(Custom("invalid length 1, expected an array of length 2"))`, but got `{:?}`"#, e ), } @@ -2784,7 +2784,7 @@ mod variable_name { assert_eq!(e, "invalid length 1, expected an array of length 3") } e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + r#"Expected `Err(Custom("invalid length 1, expected an array of length 3"))`, but got `{:?}`"#, e ), } @@ -2863,7 +2863,7 @@ mod variable_name { assert_eq!(e, "invalid length 1, expected an array of length 3") } e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + r#"Expected `Err(Custom("invalid length 1, expected an array of length 3"))`, but got `{:?}`"#, e ), } @@ -3270,7 +3270,7 @@ mod variable_name { match data { Err(DeError::Custom(e)) => assert_eq!(e, "duplicate field `$value`"), e => panic!( - r#"Expected Err(Custom("duplicate field `$value`")), got {:?}"#, + r#"Expected `Err(Custom("duplicate field `$value`"))`, but got `{:?}`"#, e ), } @@ -3316,7 +3316,7 @@ mod variable_name { assert_eq!(e, "duplicate field `$value`") } e => panic!( - r#"Expected Err(Custom("duplicate field `$value`")), got {:?}"#, + r#"Expected `Err(Custom("duplicate field `$value`"))`, but got `{:?}`"#, e ), } @@ -3410,7 +3410,7 @@ mod variable_name { match data { Err(DeError::Custom(e)) => assert_eq!(e, "duplicate field `$value`"), e => panic!( - r#"Expected Err(Custom("duplicate field `$value`")), got {:?}"#, + r#"Expected `Err(Custom("duplicate field `$value`"))`, but got `{:?}`"#, e ), } @@ -3456,7 +3456,7 @@ mod variable_name { assert_eq!(e, "duplicate field `$value`") } e => panic!( - r#"Expected Err(Custom("duplicate field `$value`")), got {:?}"#, + r#"Expected `Err(Custom("duplicate field `$value`"))`, but got `{:?}`"#, e ), } @@ -3575,7 +3575,7 @@ mod variable_name { assert_eq!(e, "duplicate field `element`") } e => panic!( - r#"Expected Err(Custom("duplicate field `element`")), got {:?}"#, + r#"Expected `Err(Custom("duplicate field `element`"))`, but got `{:?}`"#, e ), } @@ -3612,7 +3612,7 @@ mod variable_name { assert_eq!(e, "duplicate field `$value`") } e => panic!( - r#"Expected Err(Custom("duplicate field `$value`")), got {:?}"#, + r#"Expected `Err(Custom("duplicate field `$value`"))`, but got `{:?}`"#, e ), } @@ -3652,7 +3652,7 @@ mod variable_name { assert_eq!(e, "duplicate field `element`") } e => panic!( - r#"Expected Err(Custom("duplicate field `element`")), got {:?}"#, + r#"Expected `Err(Custom("duplicate field `element`"))`, but got `{:?}`"#, e ), } @@ -3692,7 +3692,7 @@ mod variable_name { assert_eq!(e, "duplicate field `$value`") } e => panic!( - r#"Expected Err(Custom("duplicate field `$value`")), got {:?}"#, + r#"Expected `Err(Custom("duplicate field `$value`"))`, but got `{:?}`"#, e ), } @@ -3807,7 +3807,7 @@ mod variable_name { assert_eq!(e, "duplicate field `element`") } e => panic!( - r#"Expected Err(Custom("duplicate field `element`")), got {:?}"#, + r#"Expected `Err(Custom("duplicate field `element`"))`, but got `{:?}`"#, e ), } @@ -3844,7 +3844,7 @@ mod variable_name { assert_eq!(e, "duplicate field `$value`") } e => panic!( - r#"Expected Err(Custom("duplicate field `$value`")), got {:?}"#, + r#"Expected `Err(Custom("duplicate field `$value`"))`, but got `{:?}`"#, e ), } @@ -3884,7 +3884,7 @@ mod variable_name { assert_eq!(e, "duplicate field `element`") } e => panic!( - r#"Expected Err(Custom("duplicate field `element`")), got {:?}"#, + r#"Expected `Err(Custom("duplicate field `element`"))`, but got `{:?}`"#, e ), } @@ -3924,7 +3924,7 @@ mod variable_name { assert_eq!(e, "duplicate field `$value`") } e => panic!( - r#"Expected Err(Custom("duplicate field `$value`")), got {:?}"#, + r#"Expected `Err(Custom("duplicate field `$value`"))`, but got `{:?}`"#, e ), } @@ -4003,7 +4003,7 @@ mod variable_name { assert_eq!(e, "invalid length 1, expected an array of length 3") } e => panic!( - r#"Expected Err(Custom("invalid length 1, expected an array of length 3")), got {:?}"#, + r#"Expected `Err(Custom("invalid length 1, expected an array of length 3"))`, but got `{:?}`"#, e ), } diff --git a/tests/serde-de.rs b/tests/serde-de.rs index b20d9067..068beaba 100644 --- a/tests/serde-de.rs +++ b/tests/serde-de.rs @@ -70,7 +70,7 @@ fn ignored_any() { let err = from_str::(""); match err { Err(DeError::UnexpectedEof) => {} - other => panic!("Expected `UnexpectedEof`, found {:?}", other), + x => panic!("Expected `Err(UnexpectedEof)`, but got `{:?}`", x), } from_str::(r#""#).unwrap(); @@ -91,9 +91,9 @@ mod trivial { #[test] fn $name() { match from_str::<$type>($value) { - Err(DeError::UnexpectedEof) => (), + Err(DeError::UnexpectedEof) => {} x => panic!( - r#"Expected `Err(DeError::UnexpectedEof)`, but got `{:?}`"#, + r#"Expected `Err(UnexpectedEof)`, but got `{:?}`"#, x ), } @@ -134,7 +134,7 @@ mod trivial { assert_eq!(msg, "binary data content is not supported by XML format") } x => panic!( - r#"Expected `Err(DeError::Unsupported("binary data content is not supported by XML format"))`, but got `{:?}`"#, + r#"Expected `Err(Unsupported("binary data content is not supported by XML format"))`, but got `{:?}`"#, x ), } @@ -148,7 +148,7 @@ mod trivial { assert_eq!(msg, "binary data content is not supported by XML format") } x => panic!( - r#"Expected `Err(DeError::Unsupported("binary data content is not supported by XML format"))`, but got `{:?}`"#, + r#"Expected `Err(Unsupported("binary data content is not supported by XML format"))`, but got `{:?}`"#, x ), } @@ -157,9 +157,9 @@ mod trivial { #[test] fn unit() { match from_str::<()>($value) { - Err(DeError::UnexpectedEof) => (), + Err(DeError::UnexpectedEof) => {} x => panic!( - r#"Expected `Err(DeError::UnexpectedEof)`, but got `{:?}`"#, + r#"Expected `Err(UnexpectedEof)`, but got `{:?}`"#, x ), } @@ -199,7 +199,7 @@ mod trivial { // Expected unexpected start element `` Err(DeError::UnexpectedStart(tag)) => assert_eq!(tag, b"nested"), x => panic!( - r#"Expected `Err(DeError::UnexpectedStart("nested"))`, but got `{:?}`"#, + r#"Expected `Err(UnexpectedStart("nested"))`, but got `{:?}`"#, x ), } @@ -208,7 +208,7 @@ mod trivial { // Expected unexpected start element `` Err(DeError::UnexpectedStart(tag)) => assert_eq!(tag, b"something-else"), x => panic!( - r#"Expected `Err(DeError::UnexpectedStart("something-else"))`, but got `{:?}`"#, + r#"Expected `Err(UnexpectedStart("something-else"))`, but got `{:?}`"#, x ), } @@ -217,7 +217,7 @@ mod trivial { // Expected unexpected start element `` Err(DeError::UnexpectedStart(tag)) => assert_eq!(tag, b"something-else"), x => panic!( - r#"Expected `Err(DeError::UnexpectedStart("something-else"))`, but got `{:?}`"#, + r#"Expected `Err(UnexpectedStart("something-else"))`, but got `{:?}`"#, x ), } @@ -232,7 +232,7 @@ mod trivial { // Expected unexpected start element `` Err(DeError::UnexpectedStart(tag)) => assert_eq!(tag, b"nested"), x => panic!( - r#"Expected `Err(DeError::UnexpectedStart("nested"))`, but got `{:?}`"#, + r#"Expected `Err(UnexpectedStart("nested"))`, but got `{:?}`"#, x ), } @@ -241,7 +241,7 @@ mod trivial { // Expected unexpected start element `` Err(DeError::UnexpectedStart(tag)) => assert_eq!(tag, b"something-else"), x => panic!( - r#"Expected `Err(DeError::UnexpectedStart("something-else"))`, but got `{:?}`"#, + r#"Expected `Err(UnexpectedStart("something-else"))`, but got `{:?}`"#, x ), } @@ -250,7 +250,7 @@ mod trivial { // Expected unexpected start element `` Err(DeError::UnexpectedStart(tag)) => assert_eq!(tag, b"something-else"), x => panic!( - r#"Expected `Err(DeError::UnexpectedStart("something-else"))`, but got `{:?}`"#, + r#"Expected `Err(UnexpectedStart("something-else"))`, but got `{:?}`"#, x ), } @@ -276,7 +276,7 @@ mod trivial { // Expected unexpected start element `` Err(DeError::Custom(reason)) => assert_eq!(reason, "missing field `$text`"), x => panic!( - r#"Expected `Err(DeError::Custom("missing field `$text`"))`, but got `{:?}`"#, + r#"Expected `Err(Custom("missing field `$text`"))`, but got `{:?}`"#, x ), } @@ -342,7 +342,7 @@ mod trivial { assert_eq!(msg, "binary data content is not supported by XML format") } x => panic!( - r#"Expected `Err(DeError::Unsupported("binary data content is not supported by XML format"))`, but got `{:?}`"#, + r#"Expected `Err(Unsupported("binary data content is not supported by XML format"))`, but got `{:?}`"#, x ), } @@ -356,7 +356,7 @@ mod trivial { assert_eq!(msg, "binary data content is not supported by XML format") } x => panic!( - r#"Expected `Err(DeError::Unsupported("binary data content is not supported by XML format"))`, but got `{:?}`"#, + r#"Expected `Err(Unsupported("binary data content is not supported by XML format"))`, but got `{:?}`"#, x ), } @@ -405,7 +405,7 @@ mod trivial { assert_eq!(msg, "binary data content is not supported by XML format") } x => panic!( - r#"Expected `Err(DeError::Unsupported("binary data content is not supported by XML format"))`, but got `{:?}`"#, + r#"Expected `Err(Unsupported("binary data content is not supported by XML format"))`, but got `{:?}`"#, x ), } @@ -419,7 +419,7 @@ mod trivial { assert_eq!(msg, "binary data content is not supported by XML format") } x => panic!( - r#"Expected `Err(DeError::Unsupported("binary data content is not supported by XML format"))`, but got `{:?}`"#, + r#"Expected `Err(Unsupported("binary data content is not supported by XML format"))`, but got `{:?}`"#, x ), } @@ -554,8 +554,8 @@ macro_rules! maplike_errors { let data = from_str::<$mixed>(r#""#); match data { - Err(DeError::UnexpectedEof) => (), - _ => panic!("Expected `UnexpectedEof`, found {:?}", data), + Err(DeError::UnexpectedEof) => {} + x => panic!("Expected `Err(UnexpectedEof)`, but got `{:?}`", x), } } @@ -564,8 +564,8 @@ macro_rules! maplike_errors { let data = from_str::<$attributes>(r#""#); match data { - Err(DeError::UnexpectedEof) => (), - _ => panic!("Expected `UnexpectedEof`, found {:?}", data), + Err(DeError::UnexpectedEof) => {} + x => panic!("Expected `Err(UnexpectedEof)`, but got `{:?}`", x), } } @@ -574,8 +574,8 @@ macro_rules! maplike_errors { let data = from_str::<$mixed>(r#"answer"#); match data { - Err(DeError::UnexpectedEof) => (), - _ => panic!("Expected `UnexpectedEof`, found {:?}", data), + Err(DeError::UnexpectedEof) => {} + x => panic!("Expected `Err(UnexpectedEof)`, but got `{:?}`", x), } } @@ -584,8 +584,8 @@ macro_rules! maplike_errors { let data = from_str::<$mixed>(r#"answer"#); match data { - Err(DeError::UnexpectedEof) => (), - _ => panic!("Expected `UnexpectedEof`, found {:?}", data), + Err(DeError::UnexpectedEof) => {} + x => panic!("Expected `Err(UnexpectedEof)`, but got `{:?}`", x), } } } @@ -601,8 +601,8 @@ macro_rules! maplike_errors { let data = from_str::<$mixed>(r#""#); match data { - Err(DeError::InvalidXml(EndEventMismatch { .. })) => (), - _ => panic!("Expected `InvalidXml(EndEventMismatch)`, found {:?}", data), + Err(DeError::InvalidXml(EndEventMismatch { .. })) => {} + x => panic!("Expected `Err(InvalidXml(EndEventMismatch {{ .. }}))`, but got `{:?}`", x), } } @@ -614,8 +614,8 @@ macro_rules! maplike_errors { ); match data { - Err(DeError::InvalidXml(EndEventMismatch { .. })) => (), - _ => panic!("Expected `InvalidXml(EndEventMismatch)`, found {:?}", data), + Err(DeError::InvalidXml(EndEventMismatch { .. })) => {} + x => panic!("Expected `Err(InvalidXml(EndEventMismatch {{ .. }}))`, but got `{:?}`", x), } } @@ -627,8 +627,8 @@ macro_rules! maplike_errors { ); match data { - Err(DeError::InvalidXml(EndEventMismatch { .. })) => (), - _ => panic!("Expected `InvalidXml(EndEventMismatch)`, found {:?}", data), + Err(DeError::InvalidXml(EndEventMismatch { .. })) => {} + x => panic!("Expected `Err(InvalidXml(EndEventMismatch {{ .. }}))`, but got `{:?}`", x), } } @@ -640,8 +640,8 @@ macro_rules! maplike_errors { ); match data { - Err(DeError::InvalidXml(EndEventMismatch { .. })) => (), - _ => panic!("Expected `InvalidXml(EndEventMismatch)`, found {:?}", data), + Err(DeError::InvalidXml(EndEventMismatch { .. })) => {} + x => panic!("Expected `Err(InvalidXml(EndEventMismatch {{ .. }}))`, but got `{:?}`", x), } } } @@ -862,8 +862,8 @@ mod struct_ { match from_str::( "\nexcess text\t42answer", ) { - Err(DeError::ExpectedStart) => (), - x => panic!("Expected Err(ExpectedStart), but got {:?}", x), + Err(DeError::ExpectedStart) => {} + x => panic!("Expected `Err(ExpectedStart)`, but got `{:?}`", x), }; } @@ -873,8 +873,8 @@ mod struct_ { match from_str::( "42answer", ) { - Err(DeError::ExpectedStart) => (), - x => panic!("Expected Err(ExpectedStart), but got {:?}", x), + Err(DeError::ExpectedStart) => {} + x => panic!("Expected `Err(ExpectedStart)`, but got `{:?}`", x), }; } @@ -1069,7 +1069,7 @@ mod xml_schema_lists { match err { DeError::$kind(e) => assert_eq!(e, $err), _ => panic!( - "Expected `{}({})`, found `{:?}`", + "Expected `Err({}({}))`, but got `{:?}`", stringify!($kind), $err, err @@ -1323,7 +1323,7 @@ mod borrow { "invalid type: string \"with escape sequence: <\", expected a borrowed string" ), e => panic!( - "Expected `Err(Custom(invalid type: string \"with escape sequence: <\", expected a borrowed string))`, but found {:?}", + r#"Expected `Err(Custom("invalid type: string \"with escape sequence: <\", expected a borrowed string"))`, but got `{:?}`"#, e ), } @@ -1342,7 +1342,7 @@ mod borrow { "invalid type: string \"with escape sequence: <\", expected a borrowed string" ), e => panic!( - "Expected `Err(Custom(invalid type: string \"with escape sequence: <\", expected a borrowed string))`, but found {:?}", + r#"Expected `Err(Custom("invalid type: string \"with escape sequence: <\", expected a borrowed string"))`, but got `{:?}`"#, e ), } @@ -1356,7 +1356,7 @@ mod borrow { "invalid type: string \"with \\\"escape\\\" sequences\", expected a borrowed string" ), e => panic!( - "Expected `Err(Custom(invalid type: string \"with \"escape\" sequences\", expected a borrowed string))`, but found {:?}", + r#"Expected `Err(Custom("invalid type: string \"with \"escape\" sequences\", expected a borrowed string"))`, but got `{:?}`"#, e ), } diff --git a/tests/serde-se.rs b/tests/serde-se.rs index 4b7fd43e..4c5d3064 100644 --- a/tests/serde-se.rs +++ b/tests/serde-se.rs @@ -255,7 +255,7 @@ mod without_root { match $data.serialize(ser) { Err(DeError::$kind(e)) => assert_eq!(e, $reason), e => panic!( - "Expected `{}({})`, found `{:?}`", + "Expected `Err({}({}))`, but got `{:?}`", stringify!($kind), $reason, e @@ -1301,7 +1301,7 @@ mod without_root { match $data.serialize(ser) { Err(DeError::$kind(e)) => assert_eq!(e, $reason), e => panic!( - "Expected `{}({})`, found `{:?}`", + "Expected `Err({}({}))`, but got `{:?}`", stringify!($kind), $reason, e @@ -1768,7 +1768,7 @@ mod with_root { match $data.serialize(ser) { Err(DeError::$kind(e)) => assert_eq!(e, $reason), e => panic!( - "Expected `{}({})`, found `{:?}`", + "Expected `Err({}({}))`, but got `{:?}`", stringify!($kind), $reason, e From da2823df6d1522d0ec33cce4f62f9cc80a4b4649 Mon Sep 17 00:00:00 2001 From: Mingun Date: Sat, 21 Oct 2023 21:33:28 +0500 Subject: [PATCH 02/24] cargo fmt Strange, that cargo_fmt did reformat the code that previously was acceptable and didn't changed in the previous commit --- tests/serde-de.rs | 56 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/tests/serde-de.rs b/tests/serde-de.rs index 068beaba..b72ef43d 100644 --- a/tests/serde-de.rs +++ b/tests/serde-de.rs @@ -225,10 +225,14 @@ mod trivial { #[test] fn field() { - let item: Field<$type> = from_str(&format!("{}", $value)).unwrap(); + let item: Field<$type> = + from_str(&format!("{}", $value)).unwrap(); assert_eq!(item, Field { value: $expected }); - match from_str::>(&format!("{}", $value)) { + match from_str::>(&format!( + "{}", + $value + )) { // Expected unexpected start element `` Err(DeError::UnexpectedStart(tag)) => assert_eq!(tag, b"nested"), x => panic!( @@ -237,7 +241,10 @@ mod trivial { ), } - match from_str::>(&format!("{}", $value)) { + match from_str::>(&format!( + "{}", + $value + )) { // Expected unexpected start element `` Err(DeError::UnexpectedStart(tag)) => assert_eq!(tag, b"something-else"), x => panic!( @@ -246,7 +253,10 @@ mod trivial { ), } - match from_str::>(&format!("{}", $value)) { + match from_str::>(&format!( + "{}", + $value + )) { // Expected unexpected start element `` Err(DeError::UnexpectedStart(tag)) => assert_eq!(tag, b"something-else"), x => panic!( @@ -259,20 +269,26 @@ mod trivial { /// Tests deserialization from top-level tag content: `...content...` #[test] fn text() { - let item: Trivial<$type> = from_str(&format!("{}", $value)).unwrap(); + let item: Trivial<$type> = + from_str(&format!("{}", $value)).unwrap(); assert_eq!(item, Trivial { value: $expected }); // Unlike `naked` test, here we have a struct that is serialized to XML with // an implicit field `$text` and some other field "something-else" which not interested // for us in the Trivial structure. If you want the same behavior as for naked primitive, // use `$value` field which would consume all data, unless a dedicated field would present - let item: Trivial<$type> = from_str(&format!("{}", $value)).unwrap(); + let item: Trivial<$type> = + from_str(&format!("{}", $value)).unwrap(); assert_eq!(item, Trivial { value: $expected }); - let item: Trivial<$type> = from_str(&format!("{}", $value)).unwrap(); + let item: Trivial<$type> = + from_str(&format!("{}", $value)).unwrap(); assert_eq!(item, Trivial { value: $expected }); - match from_str::>(&format!("{}", $value)) { + match from_str::>(&format!( + "{}", + $value + )) { // Expected unexpected start element `` Err(DeError::Custom(reason)) => assert_eq!(reason, "missing field `$text`"), x => panic!( @@ -602,7 +618,10 @@ macro_rules! maplike_errors { match data { Err(DeError::InvalidXml(EndEventMismatch { .. })) => {} - x => panic!("Expected `Err(InvalidXml(EndEventMismatch {{ .. }}))`, but got `{:?}`", x), + x => panic!( + "Expected `Err(InvalidXml(EndEventMismatch {{ .. }}))`, but got `{:?}`", + x + ), } } @@ -615,7 +634,10 @@ macro_rules! maplike_errors { match data { Err(DeError::InvalidXml(EndEventMismatch { .. })) => {} - x => panic!("Expected `Err(InvalidXml(EndEventMismatch {{ .. }}))`, but got `{:?}`", x), + x => panic!( + "Expected `Err(InvalidXml(EndEventMismatch {{ .. }}))`, but got `{:?}`", + x + ), } } @@ -628,7 +650,10 @@ macro_rules! maplike_errors { match data { Err(DeError::InvalidXml(EndEventMismatch { .. })) => {} - x => panic!("Expected `Err(InvalidXml(EndEventMismatch {{ .. }}))`, but got `{:?}`", x), + x => panic!( + "Expected `Err(InvalidXml(EndEventMismatch {{ .. }}))`, but got `{:?}`", + x + ), } } @@ -641,7 +666,10 @@ macro_rules! maplike_errors { match data { Err(DeError::InvalidXml(EndEventMismatch { .. })) => {} - x => panic!("Expected `Err(InvalidXml(EndEventMismatch {{ .. }}))`, but got `{:?}`", x), + x => panic!( + "Expected `Err(InvalidXml(EndEventMismatch {{ .. }}))`, but got `{:?}`", + x + ), } } } @@ -1315,9 +1343,7 @@ mod borrow { #[test] fn top_level() { - match from_str::<&str>( - r#"with escape sequence: <"#, - ) { + match from_str::<&str>(r#"with escape sequence: <"#) { Err(DeError::Custom(reason)) => assert_eq!( reason, "invalid type: string \"with escape sequence: <\", expected a borrowed string" From d9520c439ca27f410874f2c3a0e1808ec8e95428 Mon Sep 17 00:00:00 2001 From: Mingun Date: Sun, 8 Oct 2023 02:16:19 +0500 Subject: [PATCH 03/24] Remove `DeError::UnexpectedEnd`, because this error could be returned only if the deserializer has a bug --- src/de/map.rs | 12 ++++++++---- src/de/mod.rs | 16 +++++++++++----- src/de/var.rs | 4 +++- src/errors.rs | 13 ------------- 4 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/de/map.rs b/src/de/map.rs index 97e632da..11a84b97 100644 --- a/src/de/map.rs +++ b/src/de/map.rs @@ -669,7 +669,9 @@ where seed.deserialize(BorrowedStrDeserializer::::new(TEXT_KEY))?, true, ), - DeEvent::End(e) => return Err(DeError::UnexpectedEnd(e.name().into_inner().to_vec())), + // SAFETY: The reader is guaranteed that we don't have unmatched tags + // If we here, then out deserializer has a bug + DeEvent::End(e) => unreachable!("{:?}", e), DeEvent::Eof => return Err(DeError::UnexpectedEof), }; Ok(( @@ -927,9 +929,11 @@ 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), diff --git a/src/de/mod.rs b/src/de/mod.rs index ac0bc9ef..8e6c6c86 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -2619,7 +2619,7 @@ where /// |Event |XML |Handling /// |------------------|---------------------------|---------------------------------------- /// |[`DeEvent::Start`]|`...` |if `allow_start == true`, result determined by the second table, otherwise emits [`UnexpectedStart("tag")`](DeError::UnexpectedStart) - /// |[`DeEvent::End`] |`` |Emits [`UnexpectedEnd("any-tag")`](DeError::UnexpectedEnd) + /// |[`DeEvent::End`] |`` |This is impossible situation, the method will panic if it happens /// |[`DeEvent::Text`] |`text content` or `` (probably mixed)|Returns event content unchanged /// |[`DeEvent::Eof`] | |Emits [`UnexpectedEof`](DeError::UnexpectedEof) /// @@ -2640,7 +2640,9 @@ where // allow one nested level DeEvent::Start(_) if allow_start => self.read_text(), DeEvent::Start(e) => Err(DeError::UnexpectedStart(e.name().as_ref().to_owned())), - DeEvent::End(e) => Err(DeError::UnexpectedEnd(e.name().as_ref().to_owned())), + // SAFETY: The reader is guaranteed that we don't have unmatched tags + // If we here, then out deserializer has a bug + DeEvent::End(e) => unreachable!("{:?}", e), DeEvent::Eof => Err(DeError::UnexpectedEof), } } @@ -2816,7 +2818,9 @@ where { match self.next()? { DeEvent::Start(e) => visitor.visit_map(ElementMapAccess::new(self, e, fields)?), - DeEvent::End(e) => Err(DeError::UnexpectedEnd(e.name().as_ref().to_owned())), + // SAFETY: The reader is guaranteed that we don't have unmatched tags + // If we here, then out deserializer has a bug + DeEvent::End(e) => unreachable!("{:?}", e), DeEvent::Text(_) => Err(DeError::ExpectedStart), DeEvent::Eof => Err(DeError::UnexpectedEof), } @@ -2836,7 +2840,7 @@ where /// |Event |XML |Handling /// |------------------|---------------------------|------------------------------------------- /// |[`DeEvent::Start`]|`...` |Calls `visitor.visit_unit()`, consumes all events up to and including corresponding `End` event - /// |[`DeEvent::End`] |`` |Emits [`UnexpectedEnd("tag")`](DeError::UnexpectedEnd) + /// |[`DeEvent::End`] |`` |This is impossible situation, the method will panic if it happens /// |[`DeEvent::Text`] |`text content` or `` (probably mixed)|Calls `visitor.visit_unit()`. The content is ignored /// |[`DeEvent::Eof`] | |Emits [`UnexpectedEof`](DeError::UnexpectedEof) fn deserialize_unit(self, visitor: V) -> Result @@ -2849,7 +2853,9 @@ where visitor.visit_unit() } DeEvent::Text(_) => visitor.visit_unit(), - DeEvent::End(e) => Err(DeError::UnexpectedEnd(e.name().as_ref().to_owned())), + // SAFETY: The reader is guaranteed that we don't have unmatched tags + // If we here, then out deserializer has a bug + DeEvent::End(e) => unreachable!("{:?}", e), DeEvent::Eof => Err(DeError::UnexpectedEof), } } diff --git a/src/de/var.rs b/src/de/var.rs index 1dc335c7..f534283a 100644 --- a/src/de/var.rs +++ b/src/de/var.rs @@ -50,7 +50,9 @@ where seed.deserialize(BorrowedStrDeserializer::::new(TEXT_KEY))?, true, ), - DeEvent::End(e) => return Err(DeError::UnexpectedEnd(e.name().into_inner().to_vec())), + // SAFETY: The reader is guaranteed that we don't have unmatched tags + // If we here, then out deserializer has a bug + DeEvent::End(e) => unreachable!("{:?}", e), DeEvent::Eof => return Err(DeError::UnexpectedEof), }; Ok(( diff --git a/src/errors.rs b/src/errors.rs index 1b3a95c8..057dc618 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -196,14 +196,6 @@ pub mod serialize { /// not expecting. This happens when you try to deserialize a primitive /// value (numbers, strings, booleans) from an XML element. UnexpectedStart(Vec), - /// Deserializer encounter an end tag with a specified name when it is - /// not expecting. Usually that should not be possible, because XML reader - /// is not able to produce such stream of events that lead to this error. - /// - /// If you get this error this likely indicates and error in the `quick_xml`. - /// Please open an issue at , provide - /// your Rust code and XML input. - UnexpectedEnd(Vec), /// The [`Reader`] produced [`Event::Eof`] when it is not expecting, /// for example, after producing [`Event::Start`] but before corresponding /// [`Event::End`]. @@ -252,11 +244,6 @@ pub mod serialize { write_byte_string(f, e)?; f.write_str(")`") } - DeError::UnexpectedEnd(e) => { - f.write_str("Unexpected `Event::End(")?; - write_byte_string(f, e)?; - f.write_str(")`") - } DeError::UnexpectedEof => write!(f, "Unexpected `Event::Eof`"), DeError::ExpectedStart => write!(f, "Expecting `Event::Start`"), DeError::Unsupported(s) => write!(f, "Unsupported operation: {}", s), From 77e5a2cc88999f7a1a990c371908212695810343 Mon Sep 17 00:00:00 2001 From: Mingun Date: Sun, 8 Oct 2023 02:03:46 +0500 Subject: [PATCH 04/24] Remove `DeError::ExpectedStart`, call `Visitor::visit_borrowed_str` or `visit_string` instead It is responsibility of a `Visitor` to return an error if the provided data can not be handled --- src/de/mod.rs | 8 +++++++- src/de/text.rs | 11 ++++++++--- src/errors.rs | 7 ------- tests/serde-de.rs | 20 ++++++++++++++++---- 4 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index 8e6c6c86..90a104e0 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -2821,7 +2821,13 @@ where // SAFETY: The reader is guaranteed that we don't have unmatched tags // If we here, then out deserializer has a bug DeEvent::End(e) => unreachable!("{:?}", e), - DeEvent::Text(_) => Err(DeError::ExpectedStart), + // Deserializer methods are only hints, if deserializer could not satisfy + // request, it should return the data that it has. It is responsibility + // of a Visitor to return an error if it does not understand the data + DeEvent::Text(e) => match e.text { + Cow::Borrowed(s) => visitor.visit_borrowed_str(s), + Cow::Owned(s) => visitor.visit_string(s), + }, DeEvent::Eof => Err(DeError::UnexpectedEof), } } diff --git a/src/de/text.rs b/src/de/text.rs index 4c3b66da..f3129e48 100644 --- a/src/de/text.rs +++ b/src/de/text.rs @@ -32,7 +32,9 @@ use std::borrow::Cow; /// deserializer; /// - sequences, tuples and tuple structs are deserialized using [`SimpleTypeDeserializer`] /// (this is the difference): text content passed to the deserializer directly; -/// - structs and maps returns [`DeError::ExpectedStart`]; +/// - structs and maps calls [`Visitor::visit_borrowed_str`] or [`Visitor::visit_string`], +/// it is responsibility of the type to return an error if it do not able to process +/// this data; /// - enums: /// - the variant name is deserialized as `$text`; /// - the content is deserialized using the same deserializer: @@ -117,12 +119,15 @@ impl<'de> Deserializer<'de> for TextDeserializer<'de> { self, _name: &'static str, _fields: &'static [&'static str], - _visitor: V, + visitor: V, ) -> Result where V: Visitor<'de>, { - Err(DeError::ExpectedStart) + // Deserializer methods are only hints, if deserializer could not satisfy + // request, it should return the data that it has. It is responsibility + // of a Visitor to return an error if it does not understand the data + self.deserialize_str(visitor) } fn deserialize_enum( diff --git a/src/errors.rs b/src/errors.rs index 057dc618..1142892f 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -205,12 +205,6 @@ pub mod serialize { /// [`Event::Start`]: crate::events::Event::Start /// [`Event::End`]: crate::events::Event::End UnexpectedEof, - /// This error indicates that [`deserialize_struct`] was called, but there - /// is no any XML element in the input. That means that you try to deserialize - /// a struct not from an XML element. - /// - /// [`deserialize_struct`]: serde::de::Deserializer::deserialize_struct - ExpectedStart, /// An attempt to deserialize to a type, that is not supported by the XML /// store at current position, for example, attempt to deserialize `struct` /// from attribute or attempt to deserialize binary data. @@ -245,7 +239,6 @@ pub mod serialize { f.write_str(")`") } DeError::UnexpectedEof => write!(f, "Unexpected `Event::Eof`"), - DeError::ExpectedStart => write!(f, "Expecting `Event::Start`"), DeError::Unsupported(s) => write!(f, "Unsupported operation: {}", s), #[cfg(feature = "overlapped-lists")] DeError::TooManyEvents(s) => write!(f, "Deserializer buffers {} events, limit exceeded", s), diff --git a/tests/serde-de.rs b/tests/serde-de.rs index b72ef43d..59f30172 100644 --- a/tests/serde-de.rs +++ b/tests/serde-de.rs @@ -890,8 +890,14 @@ mod struct_ { match from_str::( "\nexcess text\t42answer", ) { - Err(DeError::ExpectedStart) => {} - x => panic!("Expected `Err(ExpectedStart)`, but got `{:?}`", x), + Err(DeError::Custom(reason)) => assert_eq!( + reason, + "invalid type: string \"excess text\", expected struct Elements", + ), + x => panic!( + r#"Expected `Err(Custom("invalid type: string \"excess text\", expected struct Elements"))`, but got `{:?}`"#, + x + ), }; } @@ -901,8 +907,14 @@ mod struct_ { match from_str::( "42answer", ) { - Err(DeError::ExpectedStart) => {} - x => panic!("Expected `Err(ExpectedStart)`, but got `{:?}`", x), + Err(DeError::Custom(reason)) => assert_eq!( + reason, + "invalid type: string \"excess cdata\", expected struct Elements", + ), + x => panic!( + r#"Expected `Err(Custom("invalid type: string \"excess cdata\", expected struct Elements"))`, but got `{:?}`"#, + x + ), }; } From a5490f26ee4f5631b4af9d69609075b84d0e8340 Mon Sep 17 00:00:00 2001 From: Mingun Date: Sat, 21 Oct 2023 00:35:44 +0500 Subject: [PATCH 05/24] Do not return `DeError::Unsupported` in `SimpleTypeDeserializer`, call `deserialize_str` instead Deserializer methods are only hints, if deserializer could not satisfy request, it should return the data that it has. It is responsibility of a Visitor to return an error if it does not understand the data UnitDeserializer::new() available only since serde 1.0.139 (serde-rs/serde#2246) --- Cargo.toml | 2 +- Changelog.md | 4 + src/de/mod.rs | 26 +++--- src/de/simple_type.rs | 199 +++++++++++++++++------------------------ tests/serde-de-enum.rs | 31 +++++-- tests/serde-de.rs | 30 +++---- 6 files changed, 142 insertions(+), 150 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b8299b91..41189800 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 } diff --git a/Changelog.md b/Changelog.md index f416a712..60403bd4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -16,6 +16,10 @@ ### Misc Changes +- [#675]: Minimum supported version of serde raised to 1.0.139 + +[#675]: https://github.com/tafia/quick-xml/pull/675 + ## 0.31.0 -- 2023-10-22 diff --git a/src/de/mod.rs b/src/de/mod.rs index 90a104e0..f0c9da7b 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -509,8 +509,9 @@ //! /// Use unit variant, if you do not care of a content. //! /// You can use tuple variant if you want to parse //! /// textual content as an xs:list. -//! /// Struct variants are not supported and will return -//! /// Err(Unsupported) +//! /// Struct variants are will pass a string to the +//! /// struct enum variant visitor, which typically +//! /// returns Err(Custom) //! #[serde(rename = "$text")] //! Text(String), //! } @@ -613,8 +614,9 @@ //! /// Use unit variant, if you do not care of a content. //! /// You can use tuple variant if you want to parse //! /// textual content as an xs:list. -//! /// Struct variants are not supported and will return -//! /// Err(Unsupported) +//! /// Struct variants are will pass a string to the +//! /// struct enum variant visitor, which typically +//! /// returns Err(Custom) //! #[serde(rename = "$text")] //! Text(String), //! } @@ -1377,9 +1379,9 @@ //! |Kind |Top-level and in `$value` field |In normal field |In `$text` field | //! |-------|-----------------------------------------|---------------------|---------------------| //! |Unit |`` |`Unit`|`Unit` | -//! |Newtype|`42` |Err(Unsupported) |Err(Unsupported) | -//! |Tuple |`42answer` |Err(Unsupported) |Err(Unsupported) | -//! |Struct |`42answer`|Err(Unsupported) |Err(Unsupported) | +//! |Newtype|`42` |Err(Custom) [^0] |Err(Custom) [^0] | +//! |Tuple |`42answer` |Err(Custom) [^0] |Err(Custom) [^0] | +//! |Struct |`42answer`|Err(Custom) [^0] |Err(Custom) [^0] | //! //! `$text` enum variant //! -------------------- @@ -1387,9 +1389,13 @@ //! |Kind |Top-level and in `$value` field |In normal field |In `$text` field | //! |-------|-----------------------------------------|---------------------|---------------------| //! |Unit |_(empty)_ |`` |_(empty)_ | -//! |Newtype|`42` |Err(Unsupported) [^1]|Err(Unsupported) [^2]| -//! |Tuple |`42 answer` |Err(Unsupported) [^3]|Err(Unsupported) [^4]| -//! |Struct |Err(Unsupported) |Err(Unsupported) |Err(Unsupported) | +//! |Newtype|`42` |Err(Custom) [^0] [^1]|Err(Custom) [^0] [^2]| +//! |Tuple |`42 answer` |Err(Custom) [^0] [^3]|Err(Custom) [^0] [^4]| +//! |Struct |Err(Custom) [^0] |Err(Custom) [^0] |Err(Custom) [^0] | +//! +//! [^0]: Error is returned by the deserialized type. In case of derived implementation a `Custom` +//! error will be returned, but custom deserialize implementation can successfully deserialize +//! value from a string which will be passed to it. //! //! [^1]: If this serialize as `42` then it will be ambiguity during deserialization, //! because it clash with `Unit` representation in normal field. diff --git a/src/de/simple_type.rs b/src/de/simple_type.rs index 52fb7410..10c38310 100644 --- a/src/de/simple_type.rs +++ b/src/de/simple_type.rs @@ -9,6 +9,7 @@ use crate::errors::serialize::DeError; use crate::escape::unescape; use crate::utils::CowRef; use memchr::memchr; +use serde::de::value::UnitDeserializer; use serde::de::{DeserializeSeed, Deserializer, EnumAccess, SeqAccess, VariantAccess, Visitor}; use serde::{self, serde_if_integer128}; use std::borrow::Cow; @@ -40,15 +41,17 @@ macro_rules! unsupported { $( ($($type:ty),*) )? - => $message:literal ) => { #[inline] fn $deserialize>( self, $($(_: $type,)*)? - _visitor: V + visitor: V ) -> Result { - Err(DeError::Unsupported($message.into())) + // Deserializer methods are only hints, if deserializer could not satisfy + // request, it should return the data that it has. It is responsibility + // of a Visitor to return an error if it does not understand the data + self.deserialize_str(visitor) } }; } @@ -138,7 +141,8 @@ impl<'de, 'a> Content<'de, 'a> { /// /// Identifiers represented as strings and deserialized accordingly. /// -/// Deserialization of all other types returns [`Unsupported`][DeError::Unsupported] error. +/// Deserialization of all other types will provide a string and in most cases +/// the deserialization will fail because visitor does not expect that. /// /// The `Owned` variant of the content acts as a storage for data, allocated by /// an external deserializer that pass it via [`ListIter`]. @@ -305,71 +309,68 @@ impl<'de, 'a> Deserializer<'de> for AtomicDeserializer<'de, 'a> { visitor.visit_unit() } - unsupported!(deserialize_bytes => "byte arrays are not supported as `xs:list` items"); - unsupported!(deserialize_byte_buf => "byte arrays are not supported as `xs:list` items"); - unsupported!(deserialize_seq => "sequences are not supported as `xs:list` items"); - unsupported!(deserialize_tuple(usize) => "tuples are not supported as `xs:list` items"); - unsupported!(deserialize_tuple_struct(&'static str, usize) => "tuples are not supported as `xs:list` items"); - unsupported!(deserialize_map => "maps are not supported as `xs:list` items"); - unsupported!(deserialize_struct(&'static str, &'static [&'static str]) => "structures are not supported as `xs:list` items"); + unsupported!(deserialize_bytes); + unsupported!(deserialize_byte_buf); + unsupported!(deserialize_seq); + unsupported!(deserialize_tuple(usize)); + unsupported!(deserialize_tuple_struct(&'static str, usize)); + unsupported!(deserialize_map); + unsupported!(deserialize_struct(&'static str, &'static [&'static str])); } impl<'de, 'a> EnumAccess<'de> for AtomicDeserializer<'de, 'a> { type Error = DeError; - type Variant = AtomicUnitOnly; + type Variant = UnitOnly; fn variant_seed(self, seed: V) -> Result<(V::Value, Self::Variant), DeError> where V: DeserializeSeed<'de>, { let name = seed.deserialize(self)?; - Ok((name, AtomicUnitOnly)) + Ok((name, UnitOnly)) } } //////////////////////////////////////////////////////////////////////////////////////////////////// /// 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 AtomicUnitOnly; -impl<'de> VariantAccess<'de> for AtomicUnitOnly { +/// Attempt to deserialize newtype will provide [`UnitDeserializer`]. +/// Attempt to deserialize tuple or struct variant will result to call of +/// [`Visitor::visit_unit`]. +pub struct UnitOnly; +impl<'de> VariantAccess<'de> for UnitOnly { type Error = DeError; #[inline] - fn unit_variant(self) -> Result<(), DeError> { + fn unit_variant(self) -> Result<(), Self::Error> { Ok(()) } - fn newtype_variant_seed(self, _seed: T) -> Result + fn newtype_variant_seed(self, seed: T) -> Result where T: DeserializeSeed<'de>, { - Err(DeError::Unsupported( - "enum newtype variants are not supported as `xs:list` items".into(), - )) + seed.deserialize(UnitDeserializer::::new()) } - fn tuple_variant(self, _len: usize, _visitor: V) -> Result + #[inline] + fn tuple_variant(self, _len: usize, visitor: V) -> Result where V: Visitor<'de>, { - Err(DeError::Unsupported( - "enum tuple variants are not supported as `xs:list` items".into(), - )) + visitor.visit_unit() } + #[inline] fn struct_variant( self, _fields: &'static [&'static str], - _visitor: V, - ) -> Result + visitor: V, + ) -> Result where V: Visitor<'de>, { - Err(DeError::Unsupported( - "enum struct variants are not supported as `xs:list` items".into(), - )) + visitor.visit_unit() } } @@ -498,10 +499,11 @@ impl<'de, 'a> SeqAccess<'de> for ListIter<'de, 'a> { /// - sequences, tuples and tuple structs are deserialized as `xs:list`s. Only /// sequences of primitive types is possible to deserialize this way and they /// should be delimited by a space (` `, `\t`, `\r`, or `\n`); -/// - structs and maps returns [`DeError::Unsupported`]; +/// - structs and maps delegates to [`Self::deserialize_str`]; /// - enums: /// - unit variants: just return `()`; -/// - all other variants returns [`DeError::Unsupported`]; +/// - newtype variants: deserialize from [`UnitDeserializer`]; +/// - tuple and struct variants: call [`Visitor::visit_unit`]; /// - identifiers are deserialized as strings. /// /// [simple types]: https://www.w3.org/TR/xmlschema11-1/#Simple_Type_Definition @@ -615,6 +617,7 @@ impl<'de, 'a> Deserializer<'de> for SimpleTypeDeserializer<'de, 'a> { deserialize_num!(deserialize_f64 => visit_f64); /// Forwards deserialization to the [`Self::deserialize_str`] + #[inline] fn deserialize_char(self, visitor: V) -> Result where V: Visitor<'de>, @@ -638,6 +641,7 @@ impl<'de, 'a> Deserializer<'de> for SimpleTypeDeserializer<'de, 'a> { } /// Forwards deserialization to the [`Self::deserialize_str`] + #[inline] fn deserialize_string(self, visitor: V) -> Result where V: Visitor<'de>, @@ -645,17 +649,17 @@ impl<'de, 'a> Deserializer<'de> for SimpleTypeDeserializer<'de, 'a> { self.deserialize_str(visitor) } - /// Returns [`DeError::Unsupported`] - fn deserialize_bytes(self, _visitor: V) -> Result + /// Forwards deserialization to the [`Self::deserialize_str`] + #[inline] + fn deserialize_bytes(self, visitor: V) -> Result where V: Visitor<'de>, { - Err(DeError::Unsupported( - "binary data content is not supported by XML format".into(), - )) + self.deserialize_str(visitor) } - /// Forwards deserialization to the [`Self::deserialize_bytes`] + /// Forwards deserialization to the [`Self::deserialize_str`] + #[inline] fn deserialize_byte_buf(self, visitor: V) -> Result where V: Visitor<'de>, @@ -670,6 +674,7 @@ impl<'de, 'a> Deserializer<'de> for SimpleTypeDeserializer<'de, 'a> { visitor.visit_some(self) } + #[inline] fn deserialize_unit(self, visitor: V) -> Result where V: Visitor<'de>, @@ -678,6 +683,7 @@ impl<'de, 'a> Deserializer<'de> for SimpleTypeDeserializer<'de, 'a> { } /// Forwards deserialization to the [`Self::deserialize_unit`] + #[inline] fn deserialize_unit_struct( self, _name: &'static str, @@ -711,6 +717,7 @@ impl<'de, 'a> Deserializer<'de> for SimpleTypeDeserializer<'de, 'a> { } /// Representation of tuples the same as [sequences][Self::deserialize_seq]. + #[inline] fn deserialize_tuple(self, _len: usize, visitor: V) -> Result where V: Visitor<'de>, @@ -719,6 +726,7 @@ impl<'de, 'a> Deserializer<'de> for SimpleTypeDeserializer<'de, 'a> { } /// Representation of named tuples the same as [unnamed tuples][Self::deserialize_tuple]. + #[inline] fn deserialize_tuple_struct( self, _name: &'static str, @@ -731,9 +739,8 @@ impl<'de, 'a> Deserializer<'de> for SimpleTypeDeserializer<'de, 'a> { self.deserialize_tuple(len, visitor) } - unsupported!(deserialize_map => "maps are not supported for XSD `simpleType`s"); - unsupported!(deserialize_struct(&'static str, &'static [&'static str]) - => "structures are not supported for XSD `simpleType`s"); + unsupported!(deserialize_map); + unsupported!(deserialize_struct(&'static str, &'static [&'static str])); fn deserialize_enum( self, @@ -748,6 +755,7 @@ impl<'de, 'a> Deserializer<'de> for SimpleTypeDeserializer<'de, 'a> { } /// Forwards deserialization to the [`Self::deserialize_str`] + #[inline] fn deserialize_identifier(self, visitor: V) -> Result where V: Visitor<'de>, @@ -755,6 +763,7 @@ impl<'de, 'a> Deserializer<'de> for SimpleTypeDeserializer<'de, 'a> { self.deserialize_str(visitor) } + #[inline] fn deserialize_ignored_any(self, visitor: V) -> Result where V: Visitor<'de>, @@ -765,60 +774,14 @@ impl<'de, 'a> Deserializer<'de> for SimpleTypeDeserializer<'de, 'a> { impl<'de, 'a> EnumAccess<'de> for SimpleTypeDeserializer<'de, 'a> { type Error = DeError; - type Variant = SimpleTypeUnitOnly; + type Variant = UnitOnly; fn variant_seed(self, seed: V) -> Result<(V::Value, Self::Variant), DeError> where V: DeserializeSeed<'de>, { let name = seed.deserialize(self)?; - Ok((name, SimpleTypeUnitOnly)) - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/// 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 SimpleTypeUnitOnly; -impl<'de> VariantAccess<'de> for SimpleTypeUnitOnly { - type Error = DeError; - - #[inline] - fn unit_variant(self) -> Result<(), DeError> { - Ok(()) - } - - fn newtype_variant_seed(self, _seed: T) -> Result - where - T: DeserializeSeed<'de>, - { - Err(DeError::Unsupported( - "enum newtype variants are not supported for XSD `simpleType`s".into(), - )) - } - - fn tuple_variant(self, _len: usize, _visitor: V) -> Result - where - V: Visitor<'de>, - { - Err(DeError::Unsupported( - "enum tuple variants are not supported for XSD `simpleType`s".into(), - )) - } - - fn struct_variant( - self, - _fields: &'static [&'static str], - _visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - Err(DeError::Unsupported( - "enum struct variants are not supported for XSD `simpleType`s".into(), - )) + Ok((name, UnitOnly)) } } @@ -1046,9 +1009,9 @@ mod tests { => Custom("invalid type: string \"escaped string\", expected a borrowed string")); err!(byte_buf: ByteBuf = "<escaped string" - => Unsupported("byte arrays are not supported as `xs:list` items")); + => Custom("invalid type: string \" Unsupported("byte arrays are not supported as `xs:list` items")); + => Custom("invalid type: string \"non-escaped string\", expected borrowed bytes")); deserialized_to!(option_none: Option<&str> = "" => None); deserialized_to!(option_some: Option<&str> = "non-escaped-string" => Some("non-escaped-string")); @@ -1063,24 +1026,24 @@ mod tests { => BorrowedNewtype("non-escaped string")); err!(seq: Vec<()> = "non-escaped string" - => Unsupported("sequences are not supported as `xs:list` items")); + => Custom("invalid type: string \"non-escaped string\", expected a sequence")); err!(tuple: ((), ()) = "non-escaped string" - => Unsupported("tuples are not supported as `xs:list` items")); + => Custom("invalid type: string \"non-escaped string\", expected a tuple of size 2")); err!(tuple_struct: Tuple = "non-escaped string" - => Unsupported("tuples are not supported as `xs:list` items")); + => Custom("invalid type: string \"non-escaped string\", expected tuple struct Tuple")); err!(map: HashMap<(), ()> = "non-escaped string" - => Unsupported("maps are not supported as `xs:list` items")); + => Custom("invalid type: string \"non-escaped string\", expected a map")); err!(struct_: Struct = "non-escaped string" - => Unsupported("structures are not supported as `xs:list` items")); + => Custom("invalid type: string \"non-escaped string\", expected struct Struct")); deserialized_to!(enum_unit: Enum = "Unit" => Enum::Unit); err!(enum_newtype: Enum = "Newtype" - => Unsupported("enum newtype variants are not supported as `xs:list` items")); + => Custom("invalid type: unit value, expected a string")); err!(enum_tuple: Enum = "Tuple" - => Unsupported("enum tuple variants are not supported as `xs:list` items")); + => Custom("invalid type: unit value, expected tuple variant Enum::Tuple")); err!(enum_struct: Enum = "Struct" - => Unsupported("enum struct variants are not supported as `xs:list` items")); + => Custom("invalid type: unit value, expected struct variant Enum::Struct")); err!(enum_other: Enum = "any data" => Custom("unknown variant `any data`, expected one of `Unit`, `Newtype`, `Tuple`, `Struct`")); @@ -1240,11 +1203,11 @@ mod tests { simple!(utf8, string: String = "<escaped string" => " Unsupported("binary data content is not supported by XML format")); + => Custom("invalid type: string \" "non-escaped string"); err!(utf8, borrowed_bytes: Bytes = "<escaped string" - => Unsupported("binary data content is not supported by XML format")); + => Custom("invalid type: string \" = "" => Some("")); simple!(utf8, option_some: Option<&str> = "non-escaped string" => Some("non-escaped string")); @@ -1262,17 +1225,17 @@ mod tests { => BorrowedNewtype("non-escaped string")); err!(utf8, map: HashMap<(), ()> = "any data" - => Unsupported("maps are not supported for XSD `simpleType`s")); + => Custom("invalid type: string \"any data\", expected a map")); err!(utf8, struct_: Struct = "any data" - => Unsupported("structures are not supported for XSD `simpleType`s")); + => Custom("invalid type: string \"any data\", expected struct Struct")); simple!(utf8, enum_unit: Enum = "Unit" => Enum::Unit); err!(utf8, enum_newtype: Enum = "Newtype" - => Unsupported("enum newtype variants are not supported for XSD `simpleType`s")); + => Custom("invalid type: unit value, expected a string")); err!(utf8, enum_tuple: Enum = "Tuple" - => Unsupported("enum tuple variants are not supported for XSD `simpleType`s")); + => Custom("invalid type: unit value, expected tuple variant Enum::Tuple")); err!(utf8, enum_struct: Enum = "Struct" - => Unsupported("enum struct variants are not supported for XSD `simpleType`s")); + => Custom("invalid type: unit value, expected struct variant Enum::Struct")); err!(utf8, enum_other: Enum = "any data" => Custom("unknown variant `any data`, expected one of `Unit`, `Newtype`, `Tuple`, `Struct`")); @@ -1301,7 +1264,7 @@ mod tests { macro_rules! unsupported { ($name:ident: $type:ty = $xml:literal => $err:literal) => { - err!(utf16, $name: $type = to_utf16($xml) => Unsupported($err)); + err!(utf16, $name: $type = to_utf16($xml) => Custom($err)); }; } @@ -1330,7 +1293,7 @@ mod tests { utf16!(string: String = "<escaped string" => " "binary data content is not supported by XML format"); + => "invalid type: string \" = "" => Some(())); utf16!(option_some: Option<()> = "any data" => Some(())); @@ -1341,23 +1304,23 @@ mod tests { utf16!(newtype_owned: Newtype = "<escaped string" => Newtype(" Custom("invalid type: string \"non-escaped string\", expected a borrowed string")); + unsupported!(newtype_borrowed: BorrowedNewtype = "non-escaped string" + => "invalid type: string \"non-escaped string\", expected a borrowed string"); unsupported!(map: HashMap<(), ()> = "any data" - => "maps are not supported for XSD `simpleType`s"); + => "invalid type: string \"any data\", expected a map"); unsupported!(struct_: Struct = "any data" - => "structures are not supported for XSD `simpleType`s"); + => "invalid type: string \"any data\", expected struct Struct"); utf16!(enum_unit: Enum = "Unit" => Enum::Unit); unsupported!(enum_newtype: Enum = "Newtype" - => "enum newtype variants are not supported for XSD `simpleType`s"); + => "invalid type: unit value, expected a string"); unsupported!(enum_tuple: Enum = "Tuple" - => "enum tuple variants are not supported for XSD `simpleType`s"); + => "invalid type: unit value, expected tuple variant Enum::Tuple"); unsupported!(enum_struct: Enum = "Struct" - => "enum struct variants are not supported for XSD `simpleType`s"); - err!(utf16, enum_other: Enum = to_utf16("any data") - => Custom("unknown variant `any data`, expected one of `Unit`, `Newtype`, `Tuple`, `Struct`")); + => "invalid type: unit value, expected struct variant Enum::Struct"); + unsupported!(enum_other: Enum = "any data" + => "unknown variant `any data`, expected one of `Unit`, `Newtype`, `Tuple`, `Struct`"); utf16!(identifier: Id = "Field" => Id::Field); utf16!(ignored_any: Any = "any data" => Any(IgnoredAny)); diff --git a/tests/serde-de-enum.rs b/tests/serde-de-enum.rs index 4b66b0cc..82028004 100644 --- a/tests/serde-de-enum.rs +++ b/tests/serde-de-enum.rs @@ -320,6 +320,7 @@ mod externally_tagged { /// Struct variant cannot be directly deserialized from `Text` / `CData` events mod struct_ { use super::*; + use pretty_assertions::assert_eq; #[derive(Debug, Deserialize, PartialEq)] enum Text { @@ -330,24 +331,42 @@ mod externally_tagged { #[test] fn text() { match from_str::(" text ") { - Err(DeError::Unsupported(_)) => {} - x => panic!("Expected `Err(Unsupported(_))`, but got `{:?}`", x), + Err(DeError::Custom(reason)) => assert_eq!( + reason, + "invalid type: string \"text\", expected struct variant Text::Struct" + ), + x => panic!( + r#"Expected `Err(Custom("invalid type: string \"text\", expected struct variant Text::Struct"))`, but got `{:?}`"#, + x + ), } } #[test] fn cdata() { match from_str::("") { - Err(DeError::Unsupported(_)) => {} - x => panic!("Expected `Err(Unsupported(_))`, but got `{:?}`", x), + Err(DeError::Custom(reason)) => assert_eq!( + reason, + "invalid type: string \" cdata \", expected struct variant Text::Struct" + ), + x => panic!( + r#"Expected `Err(Custom("invalid type: string \" cdata \", expected struct variant Text::Struct"))`, but got `{:?}`"#, + x + ), } } #[test] fn mixed() { match from_str::(" te xt ") { - Err(DeError::Unsupported(_)) => {} - x => panic!("Expected `Err(Unsupported(_))`, but got `{:?}`", x), + Err(DeError::Custom(reason)) => assert_eq!( + reason, + "invalid type: string \"te cdata xt\", expected struct variant Text::Struct" + ), + x => panic!( + r#"Expected `Err(Custom("invalid type: string \"te cdata xt\", expected struct variant Text::Struct"))`, but got `{:?}`"#, + x + ), } } } diff --git a/tests/serde-de.rs b/tests/serde-de.rs index 59f30172..09338edb 100644 --- a/tests/serde-de.rs +++ b/tests/serde-de.rs @@ -354,11 +354,11 @@ mod trivial { #[test] fn byte_buf() { match from_str::>("escaped byte_buf") { - Err(DeError::Unsupported(msg)) => { - assert_eq!(msg, "binary data content is not supported by XML format") + Err(DeError::Custom(msg)) => { + assert_eq!(msg, "invalid type: string \"escaped byte_buf\", expected byte data") } x => panic!( - r#"Expected `Err(Unsupported("binary data content is not supported by XML format"))`, but got `{:?}`"#, + r#"Expected `Err(Custom("invalid type: string \"escaped byte_buf\", expected byte data"))`, but got `{:?}`"#, x ), } @@ -368,11 +368,11 @@ mod trivial { #[test] fn bytes() { match from_str::>("escaped byte_buf") { - Err(DeError::Unsupported(msg)) => { - assert_eq!(msg, "binary data content is not supported by XML format") + Err(DeError::Custom(msg)) => { + assert_eq!(msg, "invalid type: string \"escaped byte_buf\", expected borrowed bytes") } x => panic!( - r#"Expected `Err(Unsupported("binary data content is not supported by XML format"))`, but got `{:?}`"#, + r#"Expected `Err(Custom("invalid type: string \"escaped byte_buf\", expected borrowed bytes"))`, but got `{:?}`"#, x ), } @@ -417,11 +417,11 @@ mod trivial { #[test] fn byte_buf() { match from_str::>("") { - Err(DeError::Unsupported(msg)) => { - assert_eq!(msg, "binary data content is not supported by XML format") + Err(DeError::Custom(msg)) => { + assert_eq!(msg, "invalid type: string \"escaped byte_buf\", expected byte data") } x => panic!( - r#"Expected `Err(Unsupported("binary data content is not supported by XML format"))`, but got `{:?}`"#, + r#"Expected `Err(Custom("invalid type: string \"escaped byte_buf\", expected byte data"))`, but got `{:?}`"#, x ), } @@ -431,11 +431,11 @@ mod trivial { #[test] fn bytes() { match from_str::>("") { - Err(DeError::Unsupported(msg)) => { - assert_eq!(msg, "binary data content is not supported by XML format") + Err(DeError::Custom(msg)) => { + assert_eq!(msg, "invalid type: string \"escaped byte_buf\", expected borrowed bytes") } x => panic!( - r#"Expected `Err(Unsupported("binary data content is not supported by XML format"))`, but got `{:?}`"#, + r#"Expected `Err(Custom("invalid type: string \"escaped byte_buf\", expected borrowed bytes"))`, but got `{:?}`"#, x ), } @@ -1158,7 +1158,7 @@ mod xml_schema_lists { "third 3".to_string(), ]); err!(byte_buf: ByteBuf = r#""# - => Unsupported("byte arrays are not supported as `xs:list` items")); + => Custom("invalid type: string \"first\", expected byte data")); list!(unit: () = r#""# => vec![(), (), ()]); } @@ -1209,7 +1209,7 @@ mod xml_schema_lists { "3".to_string(), ]); err!(byte_buf: ByteBuf = "first second third 3" - => Unsupported("byte arrays are not supported as `xs:list` items")); + => Custom("invalid type: string \"first\", expected byte data")); list!(unit: () = "1 second false" => vec![(), (), ()]); } @@ -1248,7 +1248,7 @@ mod xml_schema_lists { "third 3".to_string(), ]); err!(byte_buf: ByteBuf = "first second third 3" - => Unsupported("byte arrays are not supported as `xs:list` items")); + => Custom("invalid type: string \"first\", expected byte data")); list!(unit: () = "1 second false" => vec![(), (), ()]); } From f845369d31120f0b1572b1c268aa4d038263a9de Mon Sep 17 00:00:00 2001 From: Mingun Date: Sat, 21 Oct 2023 01:34:16 +0500 Subject: [PATCH 06/24] Do not return `DeError::Unsupported` in attempt to deserialize byte data Provide to visitor a string or a map instead. Deserializer methods are only hints, if deserializer could not satisfy request, it should return the data that it has. It is responsibility of a Visitor to return an error if it does not understand the data --- src/de/mod.rs | 14 +++++++++++--- tests/serde-de.rs | 14 ++++---------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index f0c9da7b..9ecc3635 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -1867,6 +1867,7 @@ macro_rules! deserialize_primitives { } /// Character represented as [strings](#method.deserialize_str). + #[inline] fn deserialize_char(self, visitor: V) -> Result where V: Visitor<'de>, @@ -1886,6 +1887,7 @@ macro_rules! deserialize_primitives { } /// Representation of owned strings the same as [non-owned](#method.deserialize_str). + #[inline] fn deserialize_string(self, visitor: V) -> Result where V: Visitor<'de>, @@ -1893,15 +1895,17 @@ macro_rules! deserialize_primitives { self.deserialize_str(visitor) } - /// Returns [`DeError::Unsupported`] - fn deserialize_bytes(self, _visitor: V) -> Result + /// Forwards deserialization to the [`deserialize_any`](#method.deserialize_any). + #[inline] + fn deserialize_bytes(self, visitor: V) -> Result where V: Visitor<'de>, { - Err(DeError::Unsupported("binary data content is not supported by XML format".into())) + self.deserialize_any(visitor) } /// Forwards deserialization to the [`deserialize_bytes`](#method.deserialize_bytes). + #[inline] fn deserialize_byte_buf(self, visitor: V) -> Result where V: Visitor<'de>, @@ -1910,6 +1914,7 @@ macro_rules! deserialize_primitives { } /// Representation of the named units the same as [unnamed units](#method.deserialize_unit). + #[inline] fn deserialize_unit_struct( self, _name: &'static str, @@ -1922,6 +1927,7 @@ macro_rules! deserialize_primitives { } /// Representation of tuples the same as [sequences](#method.deserialize_seq). + #[inline] fn deserialize_tuple(self, _len: usize, visitor: V) -> Result where V: Visitor<'de>, @@ -1930,6 +1936,7 @@ macro_rules! deserialize_primitives { } /// Representation of named tuples the same as [unnamed tuples](#method.deserialize_tuple). + #[inline] fn deserialize_tuple_struct( self, _name: &'static str, @@ -1953,6 +1960,7 @@ macro_rules! deserialize_primitives { } /// Identifiers represented as [strings](#method.deserialize_str). + #[inline] fn deserialize_identifier(self, visitor: V) -> Result where V: Visitor<'de>, diff --git a/tests/serde-de.rs b/tests/serde-de.rs index 09338edb..1921c328 100644 --- a/tests/serde-de.rs +++ b/tests/serde-de.rs @@ -130,11 +130,9 @@ mod trivial { #[test] fn byte_buf() { match from_str::($value) { - Err(DeError::Unsupported(msg)) => { - assert_eq!(msg, "binary data content is not supported by XML format") - } + Err(DeError::UnexpectedEof) => {} x => panic!( - r#"Expected `Err(Unsupported("binary data content is not supported by XML format"))`, but got `{:?}`"#, + r#"Expected `Err(UnexpectedEof)`, but got `{:?}`"#, x ), } @@ -144,11 +142,9 @@ mod trivial { #[test] fn bytes() { match from_str::($value) { - Err(DeError::Unsupported(msg)) => { - assert_eq!(msg, "binary data content is not supported by XML format") - } + Err(DeError::UnexpectedEof) => {} x => panic!( - r#"Expected `Err(Unsupported("binary data content is not supported by XML format"))`, but got `{:?}`"#, + r#"Expected `Err(UnexpectedEof)`, but got `{:?}`"#, x ), } @@ -170,7 +166,6 @@ mod trivial { /// Empty document should considered invalid no matter what type we try to deserialize mod empty_doc { use super::*; - use pretty_assertions::assert_eq; eof!(""); } @@ -178,7 +173,6 @@ mod trivial { /// Document that contains only comment should be handled as if it is empty mod only_comment { use super::*; - use pretty_assertions::assert_eq; eof!(""); } From 77a2fadb456879fadae256955c6f108fc1eac7a8 Mon Sep 17 00:00:00 2001 From: Mingun Date: Sat, 21 Oct 2023 02:07:35 +0500 Subject: [PATCH 07/24] Do not return `DeError::Unsupported` in attempt to deserialize newtype, tuple or struct from QName Provide to visitor a unit instead. Deserializer methods are only hints, if deserializer could not satisfy request, it should return the data that it has. It is responsibility of a Visitor to return an error if it does not understand the data --- src/de/key.rs | 59 ++++++--------------------------------------------- 1 file changed, 7 insertions(+), 52 deletions(-) diff --git a/src/de/key.rs b/src/de/key.rs index 9ef9d4b3..3219ef6d 100644 --- a/src/de/key.rs +++ b/src/de/key.rs @@ -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; @@ -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(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(self, _seed: T) -> Result - where - T: DeserializeSeed<'de>, - { - Err(DeError::Unsupported( - "enum newtype variants are not supported as an XML names".into(), - )) - } - - fn tuple_variant(self, _len: usize, _visitor: V) -> Result - where - V: Visitor<'de>, - { - Err(DeError::Unsupported( - "enum tuple variants are not supported as an XML names".into(), - )) - } - - fn struct_variant( - self, - _fields: &'static [&'static str], - _visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - Err(DeError::Unsupported( - "enum struct variants are not supported as an XML names".into(), - )) + Ok((name, UnitOnly)) } } @@ -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 From 29ca886de0aef818fc3ed1e83180b65ff5dab3d7 Mon Sep 17 00:00:00 2001 From: Mingun Date: Thu, 28 Sep 2023 23:02:03 +0500 Subject: [PATCH 08/24] Replace `Error::UnexpectedEof` and `Error::UnexpectedBang` by `Error::Syntax(SyntaxError)` --- Changelog.md | 3 ++ src/errors.rs | 70 ++++++++++++++++++++++--- src/lib.rs | 2 +- src/reader/mod.rs | 101 ++++++++++++++++-------------------- src/reader/slice_reader.rs | 6 +-- src/reader/state.rs | 4 +- tests/issues.rs | 15 ++---- tests/xmlrs_reader_tests.rs | 4 +- 8 files changed, 123 insertions(+), 82 deletions(-) diff --git a/Changelog.md b/Changelog.md index 60403bd4..46afe324 100644 --- a/Changelog.md +++ b/Changelog.md @@ -17,6 +17,9 @@ ### 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::UnexpectedBang` replaced by `SyntaxError` + - `Error::UnexpectedEof` replaced by `SyntaxError` in some cases [#675]: https://github.com/tafia/quick-xml/pull/675 diff --git a/src/errors.rs b/src/errors.rs index 1142892f..758ce9dd 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -9,6 +9,57 @@ use std::str::Utf8Error; use std::string::FromUtf8Error; use std::sync::Arc; +/// An error returned if parsed document does not correspond to the XML grammar, +/// for example, a tag opened by `<` not closed with `>`. This error does not +/// represent invalid XML constructs, for example, tags `<>` and `` a well-formed +/// from syntax point-of-view. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum SyntaxError { + /// The parser started to parse `` sequence was found. + UnclosedPIOrXmlDecl, + /// The parser started to parse comment (`` sequence was found. + UnclosedComment, + /// The parser started to parse DTD (`` character was found. + UnclosedDoctype, + /// The parser started to parse `` sequence was found. + UnclosedCData, + /// The parser started to parse tag content, but the input ended + /// before the closing `>` character was found. + UnclosedTag, +} + +impl fmt::Display for SyntaxError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::InvalidBangMarkup => f.write_str("unknown or missed symbol in markup"), + Self::UnclosedPIOrXmlDecl => { + f.write_str("processing instruction or xml declaration not closed: `?>` not found before end of input") + } + Self::UnclosedComment => { + f.write_str("comment not closed: `-->` not found before end of input") + } + Self::UnclosedDoctype => { + f.write_str("DOCTYPE not closed: `>` not found before end of input") + } + Self::UnclosedCData => { + f.write_str("CDATA not closed: `]]>` not found before end of input") + } + Self::UnclosedTag => f.write_str("tag not closed: `>` not found before end of input"), + } + } +} + +impl std::error::Error for SyntaxError {} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + /// The error type used by this crate. #[derive(Clone, Debug)] pub enum Error { @@ -16,6 +67,8 @@ pub enum Error { /// /// Contains the reference-counted I/O error to make the error type `Clone`able. Io(Arc), + /// The document does not corresponds to the XML grammar. + Syntax(SyntaxError), /// Input decoding error. If [`encoding`] feature is disabled, contains `None`, /// otherwise contains the UTF-8 decoding error /// @@ -32,8 +85,6 @@ pub enum Error { }, /// Unexpected token UnexpectedToken(String), - /// Unexpected - UnexpectedBang(u8), /// Text not found, expected `Event::Text` TextNotFound, /// `Event::BytesDecl` must start with *version* attribute. Contains the attribute @@ -75,6 +126,14 @@ impl From for Error { } } +impl From for Error { + /// Creates a new `Error::Syntax` from the given error + #[inline] + fn from(error: SyntaxError) -> Self { + Self::Syntax(error) + } +} + impl From for Error { /// Creates a new `Error::NonDecodable` from the given error #[inline] @@ -113,6 +172,7 @@ impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Error::Io(e) => write!(f, "I/O error: {}", e), + Error::Syntax(e) => write!(f, "syntax error: {}", e), Error::NonDecodable(None) => write!(f, "Malformed input, decoding impossible"), Error::NonDecodable(Some(e)) => write!(f, "Malformed UTF-8 input: {}", e), Error::UnexpectedEof(e) => write!(f, "Unexpected EOF during reading {}", e), @@ -120,11 +180,6 @@ impl fmt::Display for Error { write!(f, "Expecting found ", expected, found) } Error::UnexpectedToken(e) => write!(f, "Unexpected token '{}'", e), - Error::UnexpectedBang(b) => write!( - f, - "Only Comment (`--`), CDATA (`[CDATA[`) and DOCTYPE (`DOCTYPE`) nodes can start with a '!', but symbol `{}` found", - *b as char - ), Error::TextNotFound => write!(f, "Cannot read text, expecting Event::Text"), Error::XmlDeclWithoutVersion(e) => write!( f, @@ -154,6 +209,7 @@ impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { Error::Io(e) => Some(e), + Error::Syntax(e) => Some(e), Error::NonDecodable(Some(e)) => Some(e), Error::InvalidAttr(e) => Some(e), Error::EscapeError(e) => Some(e), diff --git a/src/lib.rs b/src/lib.rs index 7b0c8d26..a44898c9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,7 +54,7 @@ #[cfg(feature = "serialize")] pub mod de; pub mod encoding; -mod errors; +pub mod errors; mod escapei; pub mod escape { //! Manage xml character escapes diff --git a/src/reader/mod.rs b/src/reader/mod.rs index b63977e0..d3c5d174 100644 --- a/src/reader/mod.rs +++ b/src/reader/mod.rs @@ -5,7 +5,7 @@ use encoding_rs::Encoding; use std::ops::Range; use crate::encoding::Decoder; -use crate::errors::{Error, Result}; +use crate::errors::{Error, Result, SyntaxError}; use crate::events::Event; use crate::reader::state::ReaderState; @@ -809,8 +809,7 @@ impl BangType { Some(b'[') => Self::CData, Some(b'-') => Self::Comment, Some(b'D') | Some(b'd') => Self::DocType, - Some(b) => return Err(Error::UnexpectedBang(b)), - None => return Err(Error::UnexpectedEof("Bang".to_string())), + _ => return Err(Error::Syntax(SyntaxError::InvalidBangMarkup)), }) } @@ -877,12 +876,11 @@ impl BangType { } #[inline] fn to_err(&self) -> Error { - let bang_str = match self { - Self::CData => "CData", - Self::Comment => "Comment", - Self::DocType => "DOCTYPE", - }; - Error::UnexpectedEof(bang_str.to_string()) + match self { + Self::CData => Error::Syntax(SyntaxError::UnclosedCData), + Self::Comment => Error::Syntax(SyntaxError::UnclosedComment), + Self::DocType => Error::Syntax(SyntaxError::UnclosedDoctype), + } } } @@ -1053,13 +1051,13 @@ mod test { mod read_bang_element { use super::*; + use crate::errors::{Error, SyntaxError}; + use crate::reader::BangType; + use crate::utils::Bytes; /// Checks that reading CDATA content works correctly mod cdata { use super::*; - use crate::errors::Error; - use crate::reader::BangType; - use crate::utils::Bytes; use pretty_assertions::assert_eq; /// Checks that if input begins like CDATA element, but CDATA start sequence @@ -1073,9 +1071,9 @@ mod test { // ^= 0 match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? { - Err(Error::UnexpectedEof(s)) if s == "CData" => {} + Err(Error::Syntax(SyntaxError::UnclosedCData)) => {} x => panic!( - r#"Expected `Err(UnexpectedEof("CData"))`, but got `{:?}`"#, + "Expected `Err(Syntax(UnclosedCData))`, but got `{:?}`", x ), } @@ -1092,9 +1090,9 @@ mod test { // ^= 0 match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? { - Err(Error::UnexpectedEof(s)) if s == "CData" => {} + Err(Error::Syntax(SyntaxError::UnclosedCData)) => {} x => panic!( - r#"Expected `Err(UnexpectedEof("CData"))`, but got `{:?}`"#, + "Expected `Err(Syntax(UnclosedCData))`, but got `{:?}`", x ), } @@ -1160,9 +1158,6 @@ mod test { /// [specification]: https://www.w3.org/TR/xml11/#dt-comment mod comment { use super::*; - use crate::errors::Error; - use crate::reader::BangType; - use crate::utils::Bytes; use pretty_assertions::assert_eq; #[$test] @@ -1174,9 +1169,9 @@ mod test { // ^= 0 match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? { - Err(Error::UnexpectedEof(s)) if s == "Comment" => {} + Err(Error::Syntax(SyntaxError::UnclosedComment)) => {} x => panic!( - r#"Expected `Err(UnexpectedEof("Comment"))`, but got `{:?}`"#, + "Expected `Err(Syntax(UnclosedComment))`, but got `{:?}`", x ), } @@ -1191,9 +1186,9 @@ mod test { // ^= 0 match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? { - Err(Error::UnexpectedEof(s)) if s == "Comment" => {} + Err(Error::Syntax(SyntaxError::UnclosedComment)) => {} x => panic!( - r#"Expected `Err(UnexpectedEof("Comment"))`, but got `{:?}`"#, + "Expected `Err(Syntax(UnclosedComment))`, but got `{:?}`", x ), } @@ -1208,9 +1203,9 @@ mod test { // ^= 0 match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? { - Err(Error::UnexpectedEof(s)) if s == "Comment" => {} + Err(Error::Syntax(SyntaxError::UnclosedComment)) => {} x => panic!( - r#"Expected `Err(UnexpectedEof("Comment"))`, but got `{:?}`"#, + "Expected `Err(Syntax(UnclosedComment))`, but got `{:?}`", x ), } @@ -1225,9 +1220,9 @@ mod test { // ^= 0 match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? { - Err(Error::UnexpectedEof(s)) if s == "Comment" => {} + Err(Error::Syntax(SyntaxError::UnclosedComment)) => {} x => panic!( - r#"Expected `Err(UnexpectedEof("Comment"))`, but got `{:?}`"#, + "Expected `Err(Syntax(UnclosedComment))`, but got `{:?}`", x ), } @@ -1242,9 +1237,9 @@ mod test { // ^= 0 match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? { - Err(Error::UnexpectedEof(s)) if s == "Comment" => {} + Err(Error::Syntax(SyntaxError::UnclosedComment)) => {} x => panic!( - r#"Expected `Err(UnexpectedEof("Comment"))`, but got `{:?}`"#, + "Expected `Err(Syntax(UnclosedComment))`, but got `{:?}`", x ), } @@ -1294,9 +1289,6 @@ mod test { mod uppercase { use super::*; - use crate::errors::Error; - use crate::reader::BangType; - use crate::utils::Bytes; use pretty_assertions::assert_eq; #[$test] @@ -1307,9 +1299,9 @@ mod test { // ^= 0 match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? { - Err(Error::UnexpectedEof(s)) if s == "DOCTYPE" => {} + Err(Error::Syntax(SyntaxError::UnclosedDoctype)) => {} x => panic!( - r#"Expected `Err(UnexpectedEof("DOCTYPE"))`, but got `{:?}`"#, + "Expected `Err(Syntax(UnclosedDoctype))`, but got `{:?}`", x ), } @@ -1324,9 +1316,9 @@ mod test { // ^= 0 match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? { - Err(Error::UnexpectedEof(s)) if s == "DOCTYPE" => {} + Err(Error::Syntax(SyntaxError::UnclosedDoctype)) => {} x => panic!( - r#"Expected `Err(UnexpectedEof("DOCTYPE"))`, but got `{:?}`"#, + "Expected `Err(Syntax(UnclosedDoctype))`, but got `{:?}`", x ), } @@ -1359,9 +1351,9 @@ mod test { // ^= 0 match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? { - Err(Error::UnexpectedEof(s)) if s == "DOCTYPE" => {} + Err(Error::Syntax(SyntaxError::UnclosedDoctype)) => {} x => panic!( - r#"Expected `Err(UnexpectedEof("DOCTYPE"))`, but got `{:?}`"#, + "Expected `Err(Syntax(UnclosedDoctype))`, but got `{:?}`", x ), } @@ -1371,9 +1363,6 @@ mod test { mod lowercase { use super::*; - use crate::errors::Error; - use crate::reader::BangType; - use crate::utils::Bytes; use pretty_assertions::assert_eq; #[$test] @@ -1384,9 +1373,9 @@ mod test { // ^= 0 match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? { - Err(Error::UnexpectedEof(s)) if s == "DOCTYPE" => {} + Err(Error::Syntax(SyntaxError::UnclosedDoctype)) => {} x => panic!( - r#"Expected `Err(UnexpectedEof("DOCTYPE"))`, but got `{:?}`"#, + "Expected `Err(Syntax(UnclosedDoctype))`, but got `{:?}`", x ), } @@ -1401,9 +1390,9 @@ mod test { // ^= 0 match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? { - Err(Error::UnexpectedEof(s)) if s == "DOCTYPE" => {} + Err(Error::Syntax(SyntaxError::UnclosedDoctype)) => {} x => panic!( - r#"Expected `Err(UnexpectedEof("DOCTYPE"))`, but got `{:?}`"#, + "Expected `Err(Syntax(UnclosedDoctype))`, but got `{:?}`", x ), } @@ -1436,9 +1425,9 @@ mod test { // ^= 0 match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? { - Err(Error::UnexpectedEof(s)) if s == "DOCTYPE" => {} + Err(Error::Syntax(SyntaxError::UnclosedDoctype)) => {} x => panic!( - r#"Expected `Err(UnexpectedEof("DOCTYPE"))`, but got `{:?}`"#, + "Expected `Err(Syntax(UnclosedDoctype))`, but got `{:?}`", x ), } @@ -1622,7 +1611,7 @@ mod test { } mod issue_344 { - use crate::errors::Error; + use crate::errors::{Error, SyntaxError}; use crate::reader::Reader; #[$test] @@ -1630,9 +1619,9 @@ mod test { let mut reader = Reader::from_str("![]]>"); match reader.$read_until_close($buf) $(.$await)? { - Err(Error::UnexpectedEof(s)) if s == "CData" => {} + Err(Error::Syntax(SyntaxError::UnclosedCData)) => {} x => panic!( - r#"Expected `Err(UnexpectedEof("CData"))`, but got `{:?}`"#, + "Expected `Err(Syntax(UnclosedCData))`, but got `{:?}`", x ), } @@ -1643,9 +1632,9 @@ mod test { let mut reader = Reader::from_str("!- -->"); match reader.$read_until_close($buf) $(.$await)? { - Err(Error::UnexpectedEof(s)) if s == "Comment" => {} + Err(Error::Syntax(SyntaxError::UnclosedComment)) => {} x => panic!( - r#"Expected `Err(UnexpectedEof("Comment"))`, but got `{:?}`"#, + "Expected `Err(Syntax(UnclosedComment)`, but got `{:?}`", x ), } @@ -1656,9 +1645,9 @@ mod test { let mut reader = Reader::from_str("!D>"); match reader.$read_until_close($buf) $(.$await)? { - Err(Error::UnexpectedEof(s)) if s == "DOCTYPE" => {} + Err(Error::Syntax(SyntaxError::UnclosedDoctype)) => {} x => panic!( - r#"Expected `Err(UnexpectedEof("DOCTYPE"))`, but got `{:?}`"#, + "Expected `Err(Syntax(UnclosedDoctype))`, but got `{:?}`", x ), } @@ -1669,9 +1658,9 @@ mod test { let mut reader = Reader::from_str("!d>"); match reader.$read_until_close($buf) $(.$await)? { - Err(Error::UnexpectedEof(s)) if s == "DOCTYPE" => {} + Err(Error::Syntax(SyntaxError::UnclosedDoctype)) => {} x => panic!( - r#"Expected `Err(UnexpectedEof("DOCTYPE"))`, but got `{:?}`"#, + "Expected `Err(Syntax(UnclosedDoctype))`, but got `{:?}`", x ), } diff --git a/src/reader/slice_reader.rs b/src/reader/slice_reader.rs index aff8dcbb..8644f16f 100644 --- a/src/reader/slice_reader.rs +++ b/src/reader/slice_reader.rs @@ -9,7 +9,7 @@ use crate::reader::EncodingRef; #[cfg(feature = "encoding")] use encoding_rs::{Encoding, UTF_8}; -use crate::errors::{Error, Result}; +use crate::errors::{Error, Result, SyntaxError}; use crate::events::Event; use crate::name::QName; use crate::reader::{is_whitespace, BangType, ReadElementState, Reader, Span, XmlSource}; @@ -318,9 +318,7 @@ impl<'a> XmlSource<'a, ()> for &'a [u8] { // Note: Do not update position, so the error points to a sane place // rather than at the EOF. - Err(Error::UnexpectedEof("Element".to_string())) - - // FIXME: Figure out why the other one works without UnexpectedEof + Err(Error::Syntax(SyntaxError::UnclosedTag)) } fn skip_whitespace(&mut self, position: &mut usize) -> Result<()> { diff --git a/src/reader/state.rs b/src/reader/state.rs index c145e197..635a2dc2 100644 --- a/src/reader/state.rs +++ b/src/reader/state.rs @@ -2,7 +2,7 @@ use encoding_rs::UTF_8; use crate::encoding::Decoder; -use crate::errors::{Error, Result}; +use crate::errors::{Error, Result, SyntaxError}; use crate::events::{BytesCData, BytesDecl, BytesEnd, BytesStart, BytesText, Event}; #[cfg(feature = "encoding")] use crate::reader::EncodingRef; @@ -203,7 +203,7 @@ impl ReaderState { } } else { self.offset -= len; - Err(Error::UnexpectedEof("XmlDecl".to_string())) + Err(Error::Syntax(SyntaxError::UnclosedPIOrXmlDecl)) } } diff --git a/tests/issues.rs b/tests/issues.rs index c43f75a8..4cbf6287 100644 --- a/tests/issues.rs +++ b/tests/issues.rs @@ -4,6 +4,7 @@ use std::sync::mpsc; +use quick_xml::errors::SyntaxError; use quick_xml::events::{BytesDecl, BytesStart, BytesText, Event}; use quick_xml::name::QName; use quick_xml::reader::Reader; @@ -172,11 +173,8 @@ mod issue604 { Event::Decl(BytesDecl::new("1.0", None, None)) ); match reader.read_event_into(&mut buf) { - Err(Error::UnexpectedEof(reason)) => assert_eq!(reason, "Comment"), - x => panic!( - r#"Expected `Err(UnexpectedEof("Comment"))`, but got `{:?}`"#, - x - ), + Err(Error::Syntax(SyntaxError::UnclosedComment)) => {} + x => panic!("Expected `Err(Syntax(UnclosedComment))`, but got `{:?}`", x), } assert_eq!(reader.read_event_into(&mut buf).unwrap(), Event::Eof); } @@ -191,11 +189,8 @@ mod issue604 { Event::Decl(BytesDecl::new("1.0", None, None)) ); match reader.read_event_into(&mut buf) { - Err(Error::UnexpectedEof(reason)) => assert_eq!(reason, "Comment"), - x => panic!( - r#"Expected `Err(UnexpectedEof("Comment"))`, but got `{:?}`"#, - x - ), + Err(Error::Syntax(SyntaxError::UnclosedComment)) => {} + x => panic!("Expected `Err(Syntax(UnclosedComment))`, but got `{:?}`", x), } assert_eq!(reader.read_event_into(&mut buf).unwrap(), Event::Eof); } diff --git a/tests/xmlrs_reader_tests.rs b/tests/xmlrs_reader_tests.rs index 16c32eea..430aaedf 100644 --- a/tests/xmlrs_reader_tests.rs +++ b/tests/xmlrs_reader_tests.rs @@ -182,7 +182,7 @@ fn sample_ns_short() { fn eof_1() { test( r#"` not found before end of input"#, true, ); } @@ -191,7 +191,7 @@ fn eof_1() { fn bad_1() { test( r#"` not found before end of input"#, true, ); } From 0e5188ddbf776b095a25834ef0add50283108e6a Mon Sep 17 00:00:00 2001 From: Mingun Date: Wed, 27 Sep 2023 23:43:39 +0500 Subject: [PATCH 09/24] Replace `Error::UnexpectedEof` by `Error::IllFormed(MissedEnd)` in results of `read_to_end` Co-authored-by: Daniel Alley --- Changelog.md | 1 + src/de/mod.rs | 4 +-- src/errors.rs | 56 +++++++++++++++++++++++++++++++++-- src/reader/buffered_reader.rs | 2 +- src/reader/mod.rs | 5 +--- src/reader/ns_reader.rs | 8 ++--- src/reader/slice_reader.rs | 2 +- 7 files changed, 63 insertions(+), 15 deletions(-) diff --git a/Changelog.md b/Changelog.md index 46afe324..b4017380 100644 --- a/Changelog.md +++ b/Changelog.md @@ -20,6 +20,7 @@ - [#675]: Rework the `quick_xml::Error` type to provide more accurate information: - `Error::UnexpectedBang` replaced by `SyntaxError` - `Error::UnexpectedEof` replaced by `SyntaxError` in some cases + - `Error::UnexpectedEof` replaced by `IllFormedError` in some cases [#675]: https://github.com/tafia/quick-xml/pull/675 diff --git a/src/de/mod.rs b/src/de/mod.rs index 9ecc3635..8e00ba77 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -3055,7 +3055,7 @@ impl<'i, R: BufRead> XmlRead<'i> for IoReader { fn read_to_end(&mut self, name: QName) -> Result<(), DeError> { match self.reader.read_to_end_into(name, &mut self.buf) { - Err(Error::UnexpectedEof(_)) => Err(DeError::UnexpectedEof), + Err(Error::IllFormed(_)) => Err(DeError::UnexpectedEof), Err(e) => Err(e.into()), Ok(_) => Ok(()), } @@ -3087,7 +3087,7 @@ impl<'de> XmlRead<'de> for SliceReader<'de> { fn read_to_end(&mut self, name: QName) -> Result<(), DeError> { match self.reader.read_to_end(name) { - Err(Error::UnexpectedEof(_)) => Err(DeError::UnexpectedEof), + Err(Error::IllFormed(_)) => Err(DeError::UnexpectedEof), Err(e) => Err(e.into()), Ok(_) => Ok(()), } diff --git a/src/errors.rs b/src/errors.rs index 758ce9dd..87c734c1 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,7 +1,9 @@ //! Error management module +use crate::encoding::Decoder; use crate::escape::EscapeError; use crate::events::attributes::AttrError; +use crate::name::QName; use crate::utils::write_byte_string; use std::fmt; use std::io::Error as IoError; @@ -60,6 +62,36 @@ impl std::error::Error for SyntaxError {} //////////////////////////////////////////////////////////////////////////////////////////////////// +/// An error returned if parsed document is not [well-formed], for example, +/// an opened tag is not closed before end of input. +/// +/// [well-formed]: https://www.w3.org/TR/xml11/#dt-wellformed +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum IllFormedError { + /// The end tag was not found during reading of a sub-tree of elements due to + /// encountering an EOF from the underlying reader. This error is returned from + /// [`Reader::read_to_end`]. + /// + /// [`Reader::read_to_end`]: crate::reader::Reader::read_to_end + MissedEnd(String), +} + +impl fmt::Display for IllFormedError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::MissedEnd(tag) => write!( + f, + "start tag not closed: `` not found before end of input", + tag, + ), + } + } +} + +impl std::error::Error for IllFormedError {} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + /// The error type used by this crate. #[derive(Clone, Debug)] pub enum Error { @@ -69,13 +101,13 @@ pub enum Error { Io(Arc), /// The document does not corresponds to the XML grammar. Syntax(SyntaxError), + /// The document is not [well-formed](https://www.w3.org/TR/xml11/#dt-wellformed). + IllFormed(IllFormedError), /// Input decoding error. If [`encoding`] feature is disabled, contains `None`, /// otherwise contains the UTF-8 decoding error /// /// [`encoding`]: index.html#encoding NonDecodable(Option), - /// Unexpected End of File - UnexpectedEof(String), /// End event mismatch EndEventMismatch { /// Expected end event @@ -118,6 +150,15 @@ pub enum Error { }, } +impl Error { + pub(crate) fn missed_end(name: QName, decoder: Decoder) -> Self { + match decoder.decode(name.as_ref()) { + Ok(name) => IllFormedError::MissedEnd(name.into()).into(), + Err(err) => err.into(), + } + } +} + impl From for Error { /// Creates a new `Error::Io` from the given error #[inline] @@ -134,6 +175,14 @@ impl From for Error { } } +impl From for Error { + /// Creates a new `Error::IllFormed` from the given error + #[inline] + fn from(error: IllFormedError) -> Self { + Self::IllFormed(error) + } +} + impl From for Error { /// Creates a new `Error::NonDecodable` from the given error #[inline] @@ -173,9 +222,9 @@ impl fmt::Display for Error { match self { Error::Io(e) => write!(f, "I/O error: {}", e), Error::Syntax(e) => write!(f, "syntax error: {}", e), + Error::IllFormed(e) => write!(f, "ill-formed document: {}", e), Error::NonDecodable(None) => write!(f, "Malformed input, decoding impossible"), Error::NonDecodable(Some(e)) => write!(f, "Malformed UTF-8 input: {}", e), - Error::UnexpectedEof(e) => write!(f, "Unexpected EOF during reading {}", e), Error::EndEventMismatch { expected, found } => { write!(f, "Expecting found ", expected, found) } @@ -210,6 +259,7 @@ impl std::error::Error for Error { match self { Error::Io(e) => Some(e), Error::Syntax(e) => Some(e), + Error::IllFormed(e) => Some(e), Error::NonDecodable(Some(e)) => Some(e), Error::InvalidAttr(e) => Some(e), Error::EscapeError(e) => Some(e), diff --git a/src/reader/buffered_reader.rs b/src/reader/buffered_reader.rs index a91eb185..d2089b79 100644 --- a/src/reader/buffered_reader.rs +++ b/src/reader/buffered_reader.rs @@ -316,7 +316,7 @@ impl Reader { /// Manages nested cases where parent and child elements have the _literally_ /// same name. /// - /// If corresponding [`End`] event will not be found, the [`Error::UnexpectedEof`] + /// If a corresponding [`End`] event is not found, an error of type [`Error::IllFormed`] /// will be returned. In particularly, that error will be returned if you call /// this method without consuming the corresponding [`Start`] event first. /// diff --git a/src/reader/mod.rs b/src/reader/mod.rs index d3c5d174..8ed3ddd3 100644 --- a/src/reader/mod.rs +++ b/src/reader/mod.rs @@ -347,10 +347,7 @@ macro_rules! read_to_end { } depth -= 1; } - Ok(Event::Eof) => { - let name = $self.decoder().decode($end.as_ref()); - return Err(Error::UnexpectedEof(format!("", name))); - } + Ok(Event::Eof) => return Err(Error::missed_end($end, $self.decoder())), _ => (), } } diff --git a/src/reader/ns_reader.rs b/src/reader/ns_reader.rs index 1c05faa0..60d56a7c 100644 --- a/src/reader/ns_reader.rs +++ b/src/reader/ns_reader.rs @@ -420,7 +420,7 @@ impl NsReader { /// Manages nested cases where parent and child elements have the _literally_ /// same name. /// - /// If corresponding [`End`] event will not be found, the [`UnexpectedEof`] + /// If a corresponding [`End`] event is not found, an error of type [`IllFormed`] /// will be returned. In particularly, that error will be returned if you call /// this method without consuming the corresponding [`Start`] event first. /// @@ -501,7 +501,7 @@ impl NsReader { /// /// [`Start`]: Event::Start /// [`End`]: Event::End - /// [`UnexpectedEof`]: crate::errors::Error::UnexpectedEof + /// [`IllFormed`]: crate::errors::Error::IllFormed /// [`read_to_end()`]: Self::read_to_end /// [`BytesStart::to_end()`]: crate::events::BytesStart::to_end /// [`expand_empty_elements`]: Self::expand_empty_elements @@ -663,7 +663,7 @@ impl<'i> NsReader<&'i [u8]> { /// Manages nested cases where parent and child elements have the _literally_ /// same name. /// - /// If corresponding [`End`] event will not be found, the [`UnexpectedEof`] + /// If a corresponding [`End`] event is not found, an error of type [`IllFormed`] /// will be returned. In particularly, that error will be returned if you call /// this method without consuming the corresponding [`Start`] event first. /// @@ -738,7 +738,7 @@ impl<'i> NsReader<&'i [u8]> { /// /// [`Start`]: Event::Start /// [`End`]: Event::End - /// [`UnexpectedEof`]: crate::errors::Error::UnexpectedEof + /// [`IllFormed`]: crate::errors::Error::IllFormed /// [`BytesStart::to_end()`]: crate::events::BytesStart::to_end /// [`expand_empty_elements`]: Self::expand_empty_elements /// [the specification]: https://www.w3.org/TR/xml11/#dt-etag diff --git a/src/reader/slice_reader.rs b/src/reader/slice_reader.rs index 8644f16f..1cfdb36b 100644 --- a/src/reader/slice_reader.rs +++ b/src/reader/slice_reader.rs @@ -84,7 +84,7 @@ impl<'a> Reader<&'a [u8]> { /// Manages nested cases where parent and child elements have the _literally_ /// same name. /// - /// If corresponding [`End`] event will not be found, the [`Error::UnexpectedEof`] + /// If a corresponding [`End`] event is not found, an error of type [`Error::IllFormed`] /// will be returned. In particularly, that error will be returned if you call /// this method without consuming the corresponding [`Start`] event first. /// From f7ee91fc8f510611dc434bee6b774e4989d0e0c5 Mon Sep 17 00:00:00 2001 From: Mingun Date: Sun, 8 Oct 2023 02:39:49 +0500 Subject: [PATCH 10/24] Do not convert `DeError::InvalidXml(Error::IllFormed(MissedEnd))` to `DeError::UnexpectedEof` --- src/de/map.rs | 6 +++--- src/de/mod.rs | 33 +++++++++++++++++++++++---------- tests/serde-de.rs | 11 +++++++++-- 3 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/de/map.rs b/src/de/map.rs index 11a84b97..c3be0eb0 100644 --- a/src/de/map.rs +++ b/src/de/map.rs @@ -616,9 +616,9 @@ where if self.fixed_name { match self.map.de.next()? { // Handles UnitEnumVariant - DeEvent::Start(_) => { + DeEvent::Start(e) => { // skip , read text after it and ensure that it is ended by - let text = self.map.de.read_text()?; + let text = self.map.de.read_text(e.name())?; if text.is_empty() { // Map empty text () to a special `$text` variant visitor.visit_enum(SimpleTypeDeserializer::from_text(TEXT_KEY.into())) @@ -1022,7 +1022,7 @@ where /// [`CData`]: crate::events::Event::CData #[inline] fn read_string(&mut self) -> Result, DeError> { - self.de.read_text() + self.de.read_text(self.start.name()) } } diff --git a/src/de/mod.rs b/src/de/mod.rs index 8e00ba77..30ec1bc4 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -2644,7 +2644,7 @@ where /// |[`DeEvent::Start`]|`...` |Emits [`UnexpectedStart("any-tag")`](DeError::UnexpectedStart) /// |[`DeEvent::End`] |`` |Returns an empty slice. The reader guarantee that tag will match the open one /// |[`DeEvent::Text`] |`text content` or `` (probably mixed)|Returns event content unchanged, expects the `` after that - /// |[`DeEvent::Eof`] | |Emits [`UnexpectedEof`](DeError::UnexpectedEof) + /// |[`DeEvent::Eof`] | |Emits [`InvalidXml(IllFormed(MissedEnd))`](DeError::InvalidXml) /// /// [`Text`]: Event::Text /// [`CData`]: Event::CData @@ -2652,7 +2652,7 @@ where match self.next()? { DeEvent::Text(e) => Ok(e.text), // allow one nested level - DeEvent::Start(_) if allow_start => self.read_text(), + DeEvent::Start(e) if allow_start => self.read_text(e.name()), DeEvent::Start(e) => Err(DeError::UnexpectedStart(e.name().as_ref().to_owned())), // SAFETY: The reader is guaranteed that we don't have unmatched tags // If we here, then out deserializer has a bug @@ -2662,7 +2662,11 @@ where } /// Consumes one [`DeEvent::Text`] event and ensures that it is followed by the /// [`DeEvent::End`] event. - fn read_text(&mut self) -> Result, DeError> { + /// + /// # Parameters + /// - `name`: name of a tag opened before reading text. The corresponding end tag + /// should present in input just after the text + fn read_text(&mut self, name: QName) -> Result, DeError> { match self.next()? { DeEvent::Text(e) => match self.next()? { // The matching tag name is guaranteed by the reader @@ -2670,7 +2674,7 @@ where // SAFETY: Cannot be two consequent Text events, they would be merged into one DeEvent::Text(_) => unreachable!(), DeEvent::Start(e) => Err(DeError::UnexpectedStart(e.name().as_ref().to_owned())), - DeEvent::Eof => Err(DeError::UnexpectedEof), + DeEvent::Eof => Err(Error::missed_end(name, self.reader.decoder()).into()), }, // We can get End event in case of `` or `` input // Return empty text in that case @@ -3055,7 +3059,6 @@ impl<'i, R: BufRead> XmlRead<'i> for IoReader { fn read_to_end(&mut self, name: QName) -> Result<(), DeError> { match self.reader.read_to_end_into(name, &mut self.buf) { - Err(Error::IllFormed(_)) => Err(DeError::UnexpectedEof), Err(e) => Err(e.into()), Ok(_) => Ok(()), } @@ -3087,7 +3090,6 @@ impl<'de> XmlRead<'de> for SliceReader<'de> { fn read_to_end(&mut self, name: QName) -> Result<(), DeError> { match self.reader.read_to_end(name) { - Err(Error::IllFormed(_)) => Err(DeError::UnexpectedEof), Err(e) => Err(e.into()), Ok(_) => Ok(()), } @@ -3101,6 +3103,7 @@ impl<'de> XmlRead<'de> for SliceReader<'de> { #[cfg(test)] mod tests { use super::*; + use crate::errors::IllFormedError; use pretty_assertions::assert_eq; fn make_de<'de>(source: &'de str) -> Deserializer<'de, SliceReader<'de>> { @@ -3631,8 +3634,13 @@ mod tests { assert_eq!(de.peek().unwrap(), &Start(BytesStart::new("tag"))); match de.read_to_end(QName(b"tag")) { - Err(DeError::UnexpectedEof) => {} - x => panic!("Expected `Err(UnexpectedEof)`, but got `{:?}`", x), + Err(DeError::InvalidXml(Error::IllFormed(cause))) => { + assert_eq!(cause, IllFormedError::MissedEnd("tag".into())) + } + x => panic!( + "Expected `Err(InvalidXml(IllFormed(_)))`, but got `{:?}`", + x + ), } assert_eq!(de.next().unwrap(), Eof); } @@ -3645,8 +3653,13 @@ mod tests { assert_eq!(de.peek().unwrap(), &Text("".into())); match de.read_to_end(QName(b"tag")) { - Err(DeError::UnexpectedEof) => {} - x => panic!("Expected `Err(UnexpectedEof)`, but got `{:?}`", x), + Err(DeError::InvalidXml(Error::IllFormed(cause))) => { + assert_eq!(cause, IllFormedError::MissedEnd("tag".into())) + } + x => panic!( + "Expected `Err(InvalidXml(IllFormed(_)))`, but got `{:?}`", + x + ), } assert_eq!(de.next().unwrap(), Eof); } diff --git a/tests/serde-de.rs b/tests/serde-de.rs index 1921c328..c34c2bf4 100644 --- a/tests/serde-de.rs +++ b/tests/serde-de.rs @@ -556,6 +556,8 @@ macro_rules! maplike_errors { ) => { mod non_closed { use super::*; + use pretty_assertions::assert_eq; + use quick_xml::errors::{Error, IllFormedError}; /// For struct we expect that error about not closed tag appears /// earlier than error about missing fields @@ -594,8 +596,13 @@ macro_rules! maplike_errors { let data = from_str::<$mixed>(r#"answer"#); match data { - Err(DeError::UnexpectedEof) => {} - x => panic!("Expected `Err(UnexpectedEof)`, but got `{:?}`", x), + Err(DeError::InvalidXml(Error::IllFormed(cause))) => { + assert_eq!(cause, IllFormedError::MissedEnd("string".into())) + } + x => panic!( + "Expected `Err(InvalidXml(IllFormed(_)))`, but got `{:?}`", + x + ), } } } From a91843e7dd1bf369219cd564cb10c1175564c983 Mon Sep 17 00:00:00 2001 From: Mingun Date: Sat, 28 Oct 2023 22:31:18 +0500 Subject: [PATCH 11/24] Replace `DeError::UnexpectedEof` by `Error::IllFormed(MissedEnd)` when read text in a tag and cover that case by test --- src/de/mod.rs | 2 +- tests/serde-de.rs | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index 30ec1bc4..b266211a 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -2681,7 +2681,7 @@ where // The matching tag name is guaranteed by the reader DeEvent::End(_) => Ok("".into()), DeEvent::Start(s) => Err(DeError::UnexpectedStart(s.name().as_ref().to_owned())), - DeEvent::Eof => Err(DeError::UnexpectedEof), + DeEvent::Eof => Err(Error::missed_end(name, self.reader.decoder()).into()), } } diff --git a/tests/serde-de.rs b/tests/serde-de.rs index c34c2bf4..127b30f3 100644 --- a/tests/serde-de.rs +++ b/tests/serde-de.rs @@ -593,6 +593,20 @@ macro_rules! maplike_errors { #[test] fn elements_child() { + // Reaches `Deserializer::read_text` when text is not present + let data = from_str::<$mixed>(r#""#); + + match data { + Err(DeError::InvalidXml(Error::IllFormed(cause))) => { + assert_eq!(cause, IllFormedError::MissedEnd("string".into())) + } + x => panic!( + "Expected `Err(InvalidXml(IllFormed(_)))`, but got `{:?}`", + x + ), + } + + // Reaches `Deserializer::read_text` when text is present let data = from_str::<$mixed>(r#"answer"#); match data { From 7d20324340521de6eb42921a2155588804185e4b Mon Sep 17 00:00:00 2001 From: Mingun Date: Sun, 22 Oct 2023 01:34:32 +0500 Subject: [PATCH 12/24] Replace `DeError::UnexpectedEof` by `Error::IllFormed(MissedEnd)` in cases where Eof was received while waiting end tag --- src/de/map.rs | 3 ++- tests/serde-de.rs | 27 +++++++++++++++++++++------ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/de/map.rs b/src/de/map.rs index c3be0eb0..9a959b50 100644 --- a/src/de/map.rs +++ b/src/de/map.rs @@ -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, @@ -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()), } } } diff --git a/tests/serde-de.rs b/tests/serde-de.rs index 127b30f3..a82fa196 100644 --- a/tests/serde-de.rs +++ b/tests/serde-de.rs @@ -566,8 +566,13 @@ macro_rules! maplike_errors { let data = from_str::<$mixed>(r#""#); match data { - Err(DeError::UnexpectedEof) => {} - x => panic!("Expected `Err(UnexpectedEof)`, but got `{:?}`", x), + Err(DeError::InvalidXml(Error::IllFormed(cause))) => { + assert_eq!(cause, IllFormedError::MissedEnd("root".into())) + } + x => panic!( + "Expected `Err(InvalidXml(IllFormed(_)))`, but got `{:?}`", + x + ), } } @@ -576,8 +581,13 @@ macro_rules! maplike_errors { let data = from_str::<$attributes>(r#""#); match data { - Err(DeError::UnexpectedEof) => {} - x => panic!("Expected `Err(UnexpectedEof)`, but got `{:?}`", x), + Err(DeError::InvalidXml(Error::IllFormed(cause))) => { + assert_eq!(cause, IllFormedError::MissedEnd("root".into())) + } + x => panic!( + "Expected `Err(InvalidXml(IllFormed(_)))`, but got `{:?}`", + x + ), } } @@ -586,8 +596,13 @@ macro_rules! maplike_errors { let data = from_str::<$mixed>(r#"answer"#); match data { - Err(DeError::UnexpectedEof) => {} - x => panic!("Expected `Err(UnexpectedEof)`, but got `{:?}`", x), + Err(DeError::InvalidXml(Error::IllFormed(cause))) => { + assert_eq!(cause, IllFormedError::MissedEnd("root".into())) + } + x => panic!( + "Expected `Err(InvalidXml(IllFormed(_)))`, but got `{:?}`", + x + ), } } From 4972d4fb31ad1c86f519ed98a9c685a45462e497 Mon Sep 17 00:00:00 2001 From: Mingun Date: Sun, 22 Oct 2023 01:39:01 +0500 Subject: [PATCH 13/24] Replace `DeError::UnexpectedEof` by `Error::IllFormed(MissedEnd)` and cover case for a field of a sequential type --- src/de/map.rs | 2 +- tests/serde-de.rs | 30 +++++++++++++++++++++++++----- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/de/map.rs b/src/de/map.rs index 9a959b50..75f7f350 100644 --- a/src/de/map.rs +++ b/src/de/map.rs @@ -937,7 +937,7 @@ where } // 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), diff --git a/tests/serde-de.rs b/tests/serde-de.rs index a82fa196..271fd4ac 100644 --- a/tests/serde-de.rs +++ b/tests/serde-de.rs @@ -547,12 +547,13 @@ mod tuple_struct { // seq tests are so big, so it in the separate file serde-de-seq.rs to speed-up compilation macro_rules! maplike_errors { - ($type:ty) => { - maplike_errors!($type, $type); + ($type:ty, $list:ty) => { + maplike_errors!($type, $type, $list); }; ( $attributes:ty, - $mixed:ty + $mixed:ty, + $list:ty ) => { mod non_closed { use super::*; @@ -604,6 +605,19 @@ macro_rules! maplike_errors { x ), } + + // Reaches `DeEvent::Eof` in `MapValueSeqAccess::next_element_seed` + let data = from_str::<$list>(r#"12"#); + + match data { + Err(DeError::InvalidXml(Error::IllFormed(cause))) => { + assert_eq!(cause, IllFormedError::MissedEnd("root".into())) + } + x => panic!( + "Expected `Err(InvalidXml(IllFormed(_)))`, but got `{:?}`", + x + ), + } } #[test] @@ -755,7 +769,7 @@ mod map { ); } - maplike_errors!(HashMap<(), ()>); + maplike_errors!(HashMap<(), ()>, HashMap<(), Vec>); } mod struct_ { @@ -786,6 +800,12 @@ mod struct_ { string: String, } + /// Type where one field represented by list type + #[derive(Debug, Deserialize, PartialEq)] + struct List { + item: Vec, + } + #[test] fn elements() { let data: Elements = from_str( @@ -983,7 +1003,7 @@ mod struct_ { } } - maplike_errors!(Attributes, Mixed); + maplike_errors!(Attributes, Mixed, List); } mod nested_struct { From cee27256c8bd86e439da3ce40c3a9594eb054ee5 Mon Sep 17 00:00:00 2001 From: Mingun Date: Sun, 22 Oct 2023 01:58:24 +0500 Subject: [PATCH 14/24] `DeEvent::Eof` is impossible in `MapValueDeserializer::variant_seed`, explicitly show this using `unreachable!` when we in that branch, `Start` or `Text` event already was picked --- src/de/map.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/de/map.rs b/src/de/map.rs index 75f7f350..e229c609 100644 --- a/src/de/map.rs +++ b/src/de/map.rs @@ -670,10 +670,8 @@ where seed.deserialize(BorrowedStrDeserializer::::new(TEXT_KEY))?, true, ), - // SAFETY: The reader is guaranteed that we don't have unmatched tags - // If we here, then out deserializer has a bug - DeEvent::End(e) => unreachable!("{:?}", e), - DeEvent::Eof => return Err(DeError::UnexpectedEof), + // SAFETY: we use that deserializer only when we peeked `Start` or `Text` event + _ => unreachable!(), }; Ok(( name, From d5489dfd014338418d4267993fe8fef9fa7e0bbf Mon Sep 17 00:00:00 2001 From: Mingun Date: Sun, 8 Oct 2023 19:32:58 +0500 Subject: [PATCH 15/24] Remove unnecessary allocation --- src/se/content.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/se/content.rs b/src/se/content.rs index 777338de..91e02880 100644 --- a/src/se/content.rs +++ b/src/se/content.rs @@ -311,7 +311,7 @@ impl<'w, 'i, W: Write> Serializer for ContentSerializer<'w, 'i, W> { fn serialize_map(self, _len: Option) -> Result { Err(DeError::Unsupported( - format!("serialization of map types is not supported in `$value` field").into(), + "serialization of map types is not supported in `$value` field".into(), )) } From 00de4463b4e03d0956a95088c31ddeaee004b6df Mon Sep 17 00:00:00 2001 From: Mingun Date: Thu, 28 Sep 2023 22:03:33 +0500 Subject: [PATCH 16/24] Report trimmed found name in EndEventMismatch error when end tag has no corresponding open tag --- src/reader/state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reader/state.rs b/src/reader/state.rs index 635a2dc2..6f9f2d35 100644 --- a/src/reader/state.rs +++ b/src/reader/state.rs @@ -173,7 +173,7 @@ impl ReaderState { } None => { if self.check_end_names { - return mismatch_err("".to_string(), &buf[1..], &mut self.offset); + return mismatch_err("".to_string(), name, &mut self.offset); } } } From ff283906de6cd09e98307a751206a9f4d7bb1d88 Mon Sep 17 00:00:00 2001 From: Mingun Date: Thu, 28 Sep 2023 22:11:03 +0500 Subject: [PATCH 17/24] Report EndEventMismatch error at start of the end tag at `<` character --- src/reader/state.rs | 4 +++- tests/unit_tests.rs | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/reader/state.rs b/src/reader/state.rs index 6f9f2d35..aff9b226 100644 --- a/src/reader/state.rs +++ b/src/reader/state.rs @@ -148,7 +148,9 @@ impl ReaderState { let decoder = self.decoder(); let mismatch_err = |expected: String, found: &[u8], offset: &mut usize| { - *offset -= buf.len(); + // Report error at start of the end tag at `<` character + // +2 for `<` and `>` + *offset -= buf.len() + 2; Err(Error::EndEventMismatch { expected, found: decoder.decode(found).unwrap_or_default().into_owned(), diff --git a/tests/unit_tests.rs b/tests/unit_tests.rs index a5e8c2db..01552a18 100644 --- a/tests/unit_tests.rs +++ b/tests/unit_tests.rs @@ -404,9 +404,9 @@ fn test_offset_err_end_element() { r.trim_text(true); match r.read_event() { - Err(_) if r.buffer_position() == 2 => (), // error at char 2: no opening tag + Err(_) if r.buffer_position() == 0 => (), // error at char 0: no opening tag Err(e) => panic!( - "expecting buf_pos = 2, found {}, err: {:?}", + "expecting buf_pos = 0, found {}, err: {:?}", r.buffer_position(), e ), From 73fcf23c9f2a21ae64484da9a433abfa960d3b0a Mon Sep 17 00:00:00 2001 From: Mingun Date: Thu, 28 Sep 2023 22:16:51 +0500 Subject: [PATCH 18/24] Inline mismatch_err closure because in the next commit it will generate 2 different errors --- src/reader/state.rs | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/reader/state.rs b/src/reader/state.rs index aff9b226..c910cf36 100644 --- a/src/reader/state.rs +++ b/src/reader/state.rs @@ -147,15 +147,6 @@ impl ReaderState { }; let decoder = self.decoder(); - let mismatch_err = |expected: String, found: &[u8], offset: &mut usize| { - // Report error at start of the end tag at `<` character - // +2 for `<` and `>` - *offset -= buf.len() + 2; - Err(Error::EndEventMismatch { - expected, - found: decoder.decode(found).unwrap_or_default().into_owned(), - }) - }; // Get the index in self.opened_buffer of the name of the last opened tag match self.opened_starts.pop() { @@ -167,17 +158,28 @@ impl ReaderState { // #513: In order to allow error recovery we should drop content of the buffer self.opened_buffer.truncate(start); - return mismatch_err(expected, name, &mut self.offset); + // Report error at start of the end tag at `<` character + // +2 for `<` and `>` + self.offset -= buf.len() + 2; + return Err(Error::EndEventMismatch { + expected, + found: decoder.decode(name).unwrap_or_default().into_owned(), + }); } } self.opened_buffer.truncate(start); } - None => { - if self.check_end_names { - return mismatch_err("".to_string(), name, &mut self.offset); - } + None if self.check_end_names => { + // Report error at start of the end tag at `<` character + // +2 for `<` and `>` + self.offset -= buf.len() + 2; + return Err(Error::EndEventMismatch { + expected: "".into(), + found: decoder.decode(name).unwrap_or_default().into_owned(), + }); } + None => {} } Ok(Event::End(BytesEnd::wrap(name.into()))) From 667080087fbf3d5daff5bbe2b545c70bc9d1acf2 Mon Sep 17 00:00:00 2001 From: Mingun Date: Fri, 29 Sep 2023 01:10:24 +0500 Subject: [PATCH 19/24] Replace `Error::EndEventMismatch` by `Error::IllFormed(UnmatchedEnd)` in cases where start tag is missing --- Changelog.md | 1 + src/de/mod.rs | 77 ++++++++++++++++++++++++++------------------- src/errors.rs | 6 ++++ src/reader/state.rs | 9 +++--- 4 files changed, 56 insertions(+), 37 deletions(-) diff --git a/Changelog.md b/Changelog.md index b4017380..3eafdb03 100644 --- a/Changelog.md +++ b/Changelog.md @@ -18,6 +18,7 @@ - [#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::UnmatchedEnd` in some cases - `Error::UnexpectedBang` replaced by `SyntaxError` - `Error::UnexpectedEof` replaced by `SyntaxError` in some cases - `Error::UnexpectedEof` replaced by `IllFormedError` in some cases diff --git a/src/de/mod.rs b/src/de/mod.rs index b266211a..22650a94 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -3747,12 +3747,11 @@ mod tests { #[test] fn read_string() { match from_str::(r#""#) { - Err(DeError::InvalidXml(Error::EndEventMismatch { expected, found })) => { - assert_eq!(expected, ""); - assert_eq!(found, "root"); + Err(DeError::InvalidXml(Error::IllFormed(cause))) => { + assert_eq!(cause, IllFormedError::UnmatchedEnd("root".into())); } x => panic!( - "Expected `Err(InvalidXml(EndEventMismatch {{ expected = '', found = 'root' }}))`, but got `{:?}`", + "Expected `Err(InvalidXml(IllFormed(_)))`, but got `{:?}`", x ), } @@ -4090,11 +4089,13 @@ mod tests { assert_eq!(de.next().unwrap(), DeEvent::Start(BytesStart::new("tag"))); assert_eq!(de.next().unwrap(), DeEvent::End(BytesEnd::new("tag"))); match de.next() { - Err(DeError::InvalidXml(Error::EndEventMismatch { expected, found })) => { - assert_eq!(expected, ""); - assert_eq!(found, "tag2"); + Err(DeError::InvalidXml(Error::IllFormed(cause))) => { + assert_eq!(cause, IllFormedError::UnmatchedEnd("tag2".into())); } - x => panic!("Expected `Err(InvalidXml(EndEventMismatch {{ expected = '', found = 'tag2' }}))`, but got `{:?}`", x), + x => panic!( + "Expected `Err(InvalidXml(IllFormed(_)))`, but got `{:?}`", + x + ), } assert_eq!(de.next().unwrap(), DeEvent::Eof); } @@ -4231,11 +4232,13 @@ mod tests { fn end() { let mut de = make_de(""); match de.next() { - Err(DeError::InvalidXml(Error::EndEventMismatch { expected, found })) => { - assert_eq!(expected, ""); - assert_eq!(found, "tag"); + Err(DeError::InvalidXml(Error::IllFormed(cause))) => { + assert_eq!(cause, IllFormedError::UnmatchedEnd("tag".into())); } - x => panic!("Expected `Err(InvalidXml(EndEventMismatch {{ expected = '', found = 'tag' }}))`, but got `{:?}`", x), + x => panic!( + "Expected `Err(InvalidXml(IllFormed(_)))`, but got `{:?}`", + x + ), } assert_eq!(de.next().unwrap(), DeEvent::Eof); } @@ -4308,11 +4311,13 @@ mod tests { // Text is trimmed from both sides assert_eq!(de.next().unwrap(), DeEvent::Text("text".into())); match de.next() { - Err(DeError::InvalidXml(Error::EndEventMismatch { expected, found })) => { - assert_eq!(expected, ""); - assert_eq!(found, "tag"); + Err(DeError::InvalidXml(Error::IllFormed(cause))) => { + assert_eq!(cause, IllFormedError::UnmatchedEnd("tag".into())); } - x => panic!("Expected `Err(InvalidXml(EndEventMismatch {{ expected = '', found = 'tag' }}))`, but got `{:?}`", x), + x => panic!( + "Expected `Err(InvalidXml(IllFormed(_)))`, but got `{:?}`", + x + ), } assert_eq!(de.next().unwrap(), DeEvent::Eof); } @@ -4338,11 +4343,13 @@ mod tests { // Text is trimmed from the start assert_eq!(de.next().unwrap(), DeEvent::Text("text cdata ".into())); match de.next() { - Err(DeError::InvalidXml(Error::EndEventMismatch { expected, found })) => { - assert_eq!(expected, ""); - assert_eq!(found, "tag"); + Err(DeError::InvalidXml(Error::IllFormed(cause))) => { + assert_eq!(cause, IllFormedError::UnmatchedEnd("tag".into())); } - x => panic!("Expected `Err(InvalidXml(EndEventMismatch {{ expected = '', found = 'tag' }}))`, but got `{:?}`", x), + x => panic!( + "Expected `Err(InvalidXml(IllFormed(_)))`, but got `{:?}`", + x + ), } assert_eq!(de.next().unwrap(), DeEvent::Eof); } @@ -4442,11 +4449,13 @@ mod tests { let mut de = make_de(""); assert_eq!(de.next().unwrap(), DeEvent::Text(" cdata ".into())); match de.next() { - Err(DeError::InvalidXml(Error::EndEventMismatch { expected, found })) => { - assert_eq!(expected, ""); - assert_eq!(found, "tag"); + Err(DeError::InvalidXml(Error::IllFormed(cause))) => { + assert_eq!(cause, IllFormedError::UnmatchedEnd("tag".into())); } - x => panic!("Expected `Err(InvalidXml(EndEventMismatch {{ expected = '', found = 'tag' }}))`, but got `{:?}`", x), + x => panic!( + "Expected `Err(InvalidXml(IllFormed(_)))`, but got `{:?}`", + x + ), } assert_eq!(de.next().unwrap(), DeEvent::Eof); } @@ -4470,11 +4479,13 @@ mod tests { // Text is trimmed from the end assert_eq!(de.next().unwrap(), DeEvent::Text(" cdata text".into())); match de.next() { - Err(DeError::InvalidXml(Error::EndEventMismatch { expected, found })) => { - assert_eq!(expected, ""); - assert_eq!(found, "tag"); + Err(DeError::InvalidXml(Error::IllFormed(cause))) => { + assert_eq!(cause, IllFormedError::UnmatchedEnd("tag".into())); } - x => panic!("Expected `Err(InvalidXml(EndEventMismatch {{ expected = '', found = 'tag' }}))`, but got `{:?}`", x), + x => panic!( + "Expected `Err(InvalidXml(IllFormed(_)))`, but got `{:?}`", + x + ), } assert_eq!(de.next().unwrap(), DeEvent::Eof); } @@ -4518,11 +4529,13 @@ mod tests { let mut de = make_de(""); assert_eq!(de.next().unwrap(), DeEvent::Text(" cdata cdata2 ".into())); match de.next() { - Err(DeError::InvalidXml(Error::EndEventMismatch { expected, found })) => { - assert_eq!(expected, ""); - assert_eq!(found, "tag"); + Err(DeError::InvalidXml(Error::IllFormed(cause))) => { + assert_eq!(cause, IllFormedError::UnmatchedEnd("tag".into())); } - x => panic!("Expected `Err(InvalidXml(EndEventMismatch {{ expected = '', found = 'tag' }}))`, but got `{:?}`", x), + x => panic!( + "Expected `Err(InvalidXml(IllFormed(_)))`, but got `{:?}`", + x + ), } assert_eq!(de.next().unwrap(), DeEvent::Eof); } diff --git a/src/errors.rs b/src/errors.rs index 87c734c1..bbe29f52 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -74,6 +74,9 @@ pub enum IllFormedError { /// /// [`Reader::read_to_end`]: crate::reader::Reader::read_to_end MissedEnd(String), + /// The specified end tag was encountered without corresponding open tag at the + /// same level of hierarchy + UnmatchedEnd(String), } impl fmt::Display for IllFormedError { @@ -84,6 +87,9 @@ impl fmt::Display for IllFormedError { "start tag not closed: `` not found before end of input", tag, ), + Self::UnmatchedEnd(tag) => { + write!(f, "close tag `` does not match any open tag", tag) + } } } } diff --git a/src/reader/state.rs b/src/reader/state.rs index c910cf36..1c067190 100644 --- a/src/reader/state.rs +++ b/src/reader/state.rs @@ -2,7 +2,7 @@ use encoding_rs::UTF_8; use crate::encoding::Decoder; -use crate::errors::{Error, Result, SyntaxError}; +use crate::errors::{Error, IllFormedError, Result, SyntaxError}; use crate::events::{BytesCData, BytesDecl, BytesEnd, BytesStart, BytesText, Event}; #[cfg(feature = "encoding")] use crate::reader::EncodingRef; @@ -174,10 +174,9 @@ impl ReaderState { // Report error at start of the end tag at `<` character // +2 for `<` and `>` self.offset -= buf.len() + 2; - return Err(Error::EndEventMismatch { - expected: "".into(), - found: decoder.decode(name).unwrap_or_default().into_owned(), - }); + return Err(Error::IllFormed(IllFormedError::UnmatchedEnd( + decoder.decode(name).unwrap_or_default().into_owned(), + ))); } None => {} } From 4d887a24b55943654e78ca785992b116d82d5fd1 Mon Sep 17 00:00:00 2001 From: Mingun Date: Thu, 28 Sep 2023 23:12:49 +0500 Subject: [PATCH 20/24] Replace `Error::EndEventMismatch` by `Error::IllFormed(MismatchedEnd)` --- Changelog.md | 1 + src/de/mod.rs | 14 +++++++------- src/errors.rs | 22 ++++++++++++---------- src/reader/state.rs | 4 ++-- tests/issues.rs | 16 ++++++++-------- tests/serde-de.rs | 43 ++++++++++++++++++++++++++++++++++--------- 6 files changed, 64 insertions(+), 36 deletions(-) diff --git a/Changelog.md b/Changelog.md index 3eafdb03..926ab46c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -18,6 +18,7 @@ - [#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::UnexpectedBang` replaced by `SyntaxError` - `Error::UnexpectedEof` replaced by `SyntaxError` in some cases diff --git a/src/de/mod.rs b/src/de/mod.rs index 22650a94..d7210109 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -3760,14 +3760,14 @@ mod tests { assert_eq!(s, ""); match from_str::(r#""#) { - Err(DeError::InvalidXml(Error::EndEventMismatch { expected, found })) => { - assert_eq!(expected, "root"); - assert_eq!(found, "other"); - } - x => panic!( - "Expected `Err(InvalidXml(EndEventMismatch {{ expected = 'root', found = 'other' }}))`, but got `{:?}`", - x + Err(DeError::InvalidXml(Error::IllFormed(cause))) => assert_eq!( + cause, + IllFormedError::MismatchedEnd { + expected: "root".into(), + found: "other".into(), + } ), + x => panic!("Expected `Err(InvalidXml(IllFormed(_))`, but got `{:?}`", x), } } diff --git a/src/errors.rs b/src/errors.rs index bbe29f52..b57e74c3 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -77,6 +77,13 @@ pub enum IllFormedError { /// The specified end tag was encountered without corresponding open tag at the /// same level of hierarchy UnmatchedEnd(String), + /// The specified end tag does not match the start tag at that nesting level. + MismatchedEnd { + /// Name of open tag, that is expected to be closed + expected: String, + /// Name of actually closed tag + found: String, + }, } impl fmt::Display for IllFormedError { @@ -90,6 +97,11 @@ impl fmt::Display for IllFormedError { Self::UnmatchedEnd(tag) => { write!(f, "close tag `` does not match any open tag", tag) } + Self::MismatchedEnd { expected, found } => write!( + f, + "expected ``, but `` was found", + expected, found, + ), } } } @@ -114,13 +126,6 @@ pub enum Error { /// /// [`encoding`]: index.html#encoding NonDecodable(Option), - /// End event mismatch - EndEventMismatch { - /// Expected end event - expected: String, - /// Found end event - found: String, - }, /// Unexpected token UnexpectedToken(String), /// Text not found, expected `Event::Text` @@ -231,9 +236,6 @@ impl fmt::Display for Error { Error::IllFormed(e) => write!(f, "ill-formed document: {}", e), Error::NonDecodable(None) => write!(f, "Malformed input, decoding impossible"), Error::NonDecodable(Some(e)) => write!(f, "Malformed UTF-8 input: {}", e), - Error::EndEventMismatch { expected, found } => { - write!(f, "Expecting found ", expected, found) - } Error::UnexpectedToken(e) => write!(f, "Unexpected token '{}'", e), Error::TextNotFound => write!(f, "Cannot read text, expecting Event::Text"), Error::XmlDeclWithoutVersion(e) => write!( diff --git a/src/reader/state.rs b/src/reader/state.rs index 1c067190..cd1f8262 100644 --- a/src/reader/state.rs +++ b/src/reader/state.rs @@ -161,10 +161,10 @@ impl ReaderState { // Report error at start of the end tag at `<` character // +2 for `<` and `>` self.offset -= buf.len() + 2; - return Err(Error::EndEventMismatch { + return Err(Error::IllFormed(IllFormedError::MismatchedEnd { expected, found: decoder.decode(name).unwrap_or_default().into_owned(), - }); + })); } } diff --git a/tests/issues.rs b/tests/issues.rs index 4cbf6287..dc4103b4 100644 --- a/tests/issues.rs +++ b/tests/issues.rs @@ -4,7 +4,7 @@ use std::sync::mpsc; -use quick_xml::errors::SyntaxError; +use quick_xml::errors::{IllFormedError, SyntaxError}; use quick_xml::events::{BytesDecl, BytesStart, BytesText, Event}; use quick_xml::name::QName; use quick_xml::reader::Reader; @@ -145,14 +145,14 @@ mod issue514 { reader.check_end_names(true); match reader.read_event() { - Err(Error::EndEventMismatch { expected, found }) => { - assert_eq!(expected, "some-tag"); - assert_eq!(found, "other-tag"); - } - x => panic!( - "Expected `Err(EndEventMismatch {{ expected = 'some-tag', found = 'other-tag' }})`, but got `{:?}`", - x + Err(Error::IllFormed(cause)) => assert_eq!( + cause, + IllFormedError::MismatchedEnd { + expected: "some-tag".into(), + found: "other-tag".into(), + } ), + x => panic!("Expected `Err(IllFormed(_))`, but got `{:?}`", x), } assert_eq!(reader.read_event().unwrap(), Event::Eof); } diff --git a/tests/serde-de.rs b/tests/serde-de.rs index 271fd4ac..1aa00c95 100644 --- a/tests/serde-de.rs +++ b/tests/serde-de.rs @@ -652,7 +652,8 @@ macro_rules! maplike_errors { mod mismatched_end { use super::*; - use quick_xml::Error::EndEventMismatch; + use pretty_assertions::assert_eq; + use quick_xml::errors::{Error, IllFormedError}; /// For struct we expect that error about mismatched tag appears /// earlier than error about missing fields @@ -661,9 +662,15 @@ macro_rules! maplike_errors { let data = from_str::<$mixed>(r#""#); match data { - Err(DeError::InvalidXml(EndEventMismatch { .. })) => {} + Err(DeError::InvalidXml(Error::IllFormed(cause))) => assert_eq!( + cause, + IllFormedError::MismatchedEnd { + expected: "root".into(), + found: "mismatched".into(), + } + ), x => panic!( - "Expected `Err(InvalidXml(EndEventMismatch {{ .. }}))`, but got `{:?}`", + "Expected `Err(InvalidXml(IllFormed(_)))`, but got `{:?}`", x ), } @@ -677,9 +684,15 @@ macro_rules! maplike_errors { ); match data { - Err(DeError::InvalidXml(EndEventMismatch { .. })) => {} + Err(DeError::InvalidXml(Error::IllFormed(cause))) => assert_eq!( + cause, + IllFormedError::MismatchedEnd { + expected: "root".into(), + found: "mismatched".into(), + } + ), x => panic!( - "Expected `Err(InvalidXml(EndEventMismatch {{ .. }}))`, but got `{:?}`", + "Expected `Err(InvalidXml(IllFormed(_)))`, but got `{:?}`", x ), } @@ -693,9 +706,15 @@ macro_rules! maplike_errors { ); match data { - Err(DeError::InvalidXml(EndEventMismatch { .. })) => {} + Err(DeError::InvalidXml(Error::IllFormed(cause))) => assert_eq!( + cause, + IllFormedError::MismatchedEnd { + expected: "root".into(), + found: "mismatched".into(), + } + ), x => panic!( - "Expected `Err(InvalidXml(EndEventMismatch {{ .. }}))`, but got `{:?}`", + "Expected `Err(InvalidXml(IllFormed(_)))`, but got `{:?}`", x ), } @@ -709,9 +728,15 @@ macro_rules! maplike_errors { ); match data { - Err(DeError::InvalidXml(EndEventMismatch { .. })) => {} + Err(DeError::InvalidXml(Error::IllFormed(cause))) => assert_eq!( + cause, + IllFormedError::MismatchedEnd { + expected: "string".into(), + found: "mismatched".into(), + } + ), x => panic!( - "Expected `Err(InvalidXml(EndEventMismatch {{ .. }}))`, but got `{:?}`", + "Expected `Err(InvalidXml(IllFormed(_)))`, but got `{:?}`", x ), } From a4febadb63ed100fff0f00a479a126b89426032a Mon Sep 17 00:00:00 2001 From: Mingun Date: Fri, 29 Sep 2023 23:01:17 +0500 Subject: [PATCH 21/24] `IllFormed(UnmatchedEnd)` should be reported always because lone closed tags are not allowed even in HTML --- src/reader/mod.rs | 13 +++---------- src/reader/state.rs | 3 +-- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/reader/mod.rs b/src/reader/mod.rs index 8ed3ddd3..e7b9c37a 100644 --- a/src/reader/mod.rs +++ b/src/reader/mod.rs @@ -1740,22 +1740,15 @@ mod test { ); } + /// Lone closing tags are not allowed, so testing it together with start tag #[$test] - $($async)? fn start() { - let mut reader = Reader::from_str(""); + $($async)? fn start_and_end() { + let mut reader = Reader::from_str(""); assert_eq!( reader.$read_event($buf) $(.$await)? .unwrap(), Event::Start(BytesStart::new("tag")) ); - } - - #[$test] - $($async)? fn end() { - let mut reader = Reader::from_str(""); - // Because we expect invalid XML, do not check that - // the end name paired with the start name - reader.check_end_names(false); assert_eq!( reader.$read_event($buf) $(.$await)? .unwrap(), diff --git a/src/reader/state.rs b/src/reader/state.rs index cd1f8262..4b3c1f8b 100644 --- a/src/reader/state.rs +++ b/src/reader/state.rs @@ -170,7 +170,7 @@ impl ReaderState { self.opened_buffer.truncate(start); } - None if self.check_end_names => { + None => { // Report error at start of the end tag at `<` character // +2 for `<` and `>` self.offset -= buf.len() + 2; @@ -178,7 +178,6 @@ impl ReaderState { decoder.decode(name).unwrap_or_default().into_owned(), ))); } - None => {} } Ok(Event::End(BytesEnd::wrap(name.into()))) From 596edd666d31c6bad9bdb11751a7e27d6bce215e Mon Sep 17 00:00:00 2001 From: Mingun Date: Sun, 29 Oct 2023 00:47:53 +0500 Subject: [PATCH 22/24] Replace `Error::UnexpectedToken` by `Error::IllFormed(DoubleHyphenInComment)` --- Changelog.md | 1 + examples/read_nodes.rs | 29 +++++++++++++++++++++++++---- src/errors.rs | 17 ++++++++++++++--- src/reader/state.rs | 2 +- tests/xmlrs_reader_tests.rs | 4 ++-- 5 files changed, 43 insertions(+), 10 deletions(-) diff --git a/Changelog.md b/Changelog.md index 926ab46c..68219f99 100644 --- a/Changelog.md +++ b/Changelog.md @@ -23,6 +23,7 @@ - `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 diff --git a/examples/read_nodes.rs b/examples/read_nodes.rs index e7ea77e6..07a7ad6b 100644 --- a/examples/read_nodes.rs +++ b/examples/read_nodes.rs @@ -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; @@ -29,6 +30,26 @@ const XML: &str = r#" "#; +#[derive(Debug)] +enum AppError { + /// XML parsing error + Xml(quick_xml::Error), + /// The `Translation/Text` node is missed + NoText(String), +} + +impl From for AppError { + fn from(error: quick_xml::Error) -> Self { + Self::Xml(error) + } +} + +impl From for AppError { + fn from(error: AttrError) -> Self { + Self::Xml(quick_xml::Error::InvalidAttr(error)) + } +} + #[derive(Debug)] struct Translation { tag: String, @@ -40,7 +61,7 @@ impl Translation { fn new_from_element( reader: &mut Reader<&[u8]>, element: BytesStart, - ) -> Result { + ) -> Result { let mut tag = Cow::Borrowed(""); let mut lang = Cow::Borrowed(""); @@ -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; diff --git a/src/errors.rs b/src/errors.rs index b57e74c3..b6196471 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -84,6 +84,17 @@ pub enum IllFormedError { /// Name of actually closed tag found: String, }, + /// A comment contains forbidden double-hyphen (`--`) sequence inside. + /// + /// According to the [specification], for compatibility, comments MUST NOT contain + /// double-hyphen (`--`) sequence, in particular, they cannot end by `--->`. + /// + /// The quick-xml by default does not check that, because this restriction is + /// mostly artificial, but you can enable it in the [configuration]. + /// + /// [specification]: https://www.w3.org/TR/xml11/#sec-comments + /// [configuration]: crate::reader::Reader::check_comments + DoubleHyphenInComment, } impl fmt::Display for IllFormedError { @@ -102,6 +113,9 @@ impl fmt::Display for IllFormedError { "expected ``, but `` was found", expected, found, ), + Self::DoubleHyphenInComment => { + write!(f, "forbidden string `--` was found in a comment") + } } } } @@ -126,8 +140,6 @@ pub enum Error { /// /// [`encoding`]: index.html#encoding NonDecodable(Option), - /// Unexpected token - UnexpectedToken(String), /// Text not found, expected `Event::Text` TextNotFound, /// `Event::BytesDecl` must start with *version* attribute. Contains the attribute @@ -236,7 +248,6 @@ impl fmt::Display for Error { Error::IllFormed(e) => write!(f, "ill-formed document: {}", e), Error::NonDecodable(None) => write!(f, "Malformed input, decoding impossible"), Error::NonDecodable(Some(e)) => write!(f, "Malformed UTF-8 input: {}", e), - Error::UnexpectedToken(e) => write!(f, "Unexpected token '{}'", e), Error::TextNotFound => write!(f, "Cannot read text, expecting Event::Text"), Error::XmlDeclWithoutVersion(e) => write!( f, diff --git a/src/reader/state.rs b/src/reader/state.rs index 4b3c1f8b..6169cf63 100644 --- a/src/reader/state.rs +++ b/src/reader/state.rs @@ -97,7 +97,7 @@ impl ReaderState { .position(|p| buf[3 + p + 1] == b'-') { self.offset += len - p; - return Err(Error::UnexpectedToken("--".to_string())); + return Err(Error::IllFormed(IllFormedError::DoubleHyphenInComment)); } } Ok(Event::Comment(BytesText::wrap( diff --git a/tests/xmlrs_reader_tests.rs b/tests/xmlrs_reader_tests.rs index 430aaedf..5fc08581 100644 --- a/tests/xmlrs_reader_tests.rs +++ b/tests/xmlrs_reader_tests.rs @@ -201,7 +201,7 @@ fn dashes_in_comments() { test( r#""#, r#" - |Error: Unexpected token '--' + |Error: ill-formed document: forbidden string `--` was found in a comment "#, true, ); @@ -209,7 +209,7 @@ fn dashes_in_comments() { test( r#""#, r#" - |Error: Unexpected token '--' + |Error: ill-formed document: forbidden string `--` was found in a comment "#, true, ); From 53c6b4e4ba55babe8be13b715124d61abf9c3dc9 Mon Sep 17 00:00:00 2001 From: Mingun Date: Sun, 29 Oct 2023 00:50:09 +0500 Subject: [PATCH 23/24] Remove not used `Error::TextNotFound` The only usage was removed in 792d23d9b0a2207ebd740cdbf599ee2ee4310017 in #445 --- Changelog.md | 1 + src/errors.rs | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 68219f99..40cce462 100644 --- a/Changelog.md +++ b/Changelog.md @@ -20,6 +20,7 @@ - [#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 diff --git a/src/errors.rs b/src/errors.rs index b6196471..9155a066 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -140,8 +140,6 @@ pub enum Error { /// /// [`encoding`]: index.html#encoding NonDecodable(Option), - /// Text not found, expected `Event::Text` - TextNotFound, /// `Event::BytesDecl` must start with *version* attribute. Contains the attribute /// that was found or `None` if an xml declaration doesn't contain attributes. XmlDeclWithoutVersion(Option), @@ -248,7 +246,6 @@ impl fmt::Display for Error { Error::IllFormed(e) => write!(f, "ill-formed document: {}", e), Error::NonDecodable(None) => write!(f, "Malformed input, decoding impossible"), Error::NonDecodable(Some(e)) => write!(f, "Malformed UTF-8 input: {}", e), - Error::TextNotFound => write!(f, "Cannot read text, expecting Event::Text"), Error::XmlDeclWithoutVersion(e) => write!( f, "XmlDecl must start with 'version' attribute, found {:?}", From a049cd2d18b8d5144de79a11ca186a41237d034c Mon Sep 17 00:00:00 2001 From: Mingun Date: Sun, 29 Oct 2023 01:25:30 +0500 Subject: [PATCH 24/24] Better document `Error::XmlDeclWithoutVersion` and `Error::EmptyDocType` Co-authored-by: Daniel Alley --- src/errors.rs | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index 9155a066..0893edca 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -140,12 +140,24 @@ pub enum Error { /// /// [`encoding`]: index.html#encoding NonDecodable(Option), - /// `Event::BytesDecl` must start with *version* attribute. Contains the attribute - /// that was found or `None` if an xml declaration doesn't contain attributes. + /// A `version` attribute was not found in an XML declaration or is not the + /// first attribute. + /// + /// According to the [specification], the XML declaration (``) MUST contain + /// a `version` attribute and it MUST be the first attribute. This error indicates, + /// that the declaration does not contain attributes at all (if contains `None`) + /// or either `version` attribute is not present or not the first attribute in + /// the declaration. In the last case it contains the name of the found attribute. + /// + /// [specification]: https://www.w3.org/TR/xml11/#sec-prolog-dtd XmlDeclWithoutVersion(Option), - /// Empty `Event::DocType`. `` is correct but ` is not. + /// A document type definition (DTD) does not contain a name of a root element. + /// + /// According to the [specification], document type definition (``) + /// MUST contain a name which defines a document type. If that name is missed, + /// this error is returned. /// - /// See + /// [specification]: https://www.w3.org/TR/xml11/#NT-doctypedecl EmptyDocType, /// Attribute parsing error InvalidAttr(AttrError), @@ -246,12 +258,16 @@ impl fmt::Display for Error { Error::IllFormed(e) => write!(f, "ill-formed document: {}", e), Error::NonDecodable(None) => write!(f, "Malformed input, decoding impossible"), Error::NonDecodable(Some(e)) => write!(f, "Malformed UTF-8 input: {}", e), - Error::XmlDeclWithoutVersion(e) => write!( + Error::XmlDeclWithoutVersion(None) => { + write!(f, "an XML declaration does not contain `version` attribute") + } + Error::XmlDeclWithoutVersion(Some(attr)) => { + write!(f, "an XML declaration must start with `version` attribute, but in starts with `{}`", attr) + } + Error::EmptyDocType => write!( f, - "XmlDecl must start with 'version' attribute, found {:?}", - e + "`` declaration does not contain a name of a document type" ), - Error::EmptyDocType => write!(f, "DOCTYPE declaration must not be empty"), Error::InvalidAttr(e) => write!(f, "error while parsing attribute: {}", e), Error::EscapeError(e) => write!(f, "{}", e), Error::UnknownPrefix(prefix) => {