Attach location info to errors
diff --git a/third_party/Cargo.toml b/third_party/Cargo.toml
index b0cd7c5..e1577d8 100644
--- a/third_party/Cargo.toml
+++ b/third_party/Cargo.toml
@@ -15,5 +15,6 @@
serde = "1.0"
[dev-dependencies]
+matches = "0.1.8"
serde_derive = "1.0"
serde_json = "1.0"
diff --git a/third_party/src/de.rs b/third_party/src/de.rs
index 8596f3a..bfe06ec 100644
--- a/third_party/src/de.rs
+++ b/third_party/src/de.rs
@@ -7,7 +7,7 @@
use std::collections::VecDeque;
use std::f64;
-use crate::error::{Error, Result};
+use crate::error::{self, Error, Result};
#[derive(Parser)]
#[grammar = "json5.pest"]
@@ -48,21 +48,26 @@
V: de::Visitor<'de>,
{
let pair = self.pair.take().unwrap();
- match pair.as_rule() {
- Rule::null => visitor.visit_unit(),
- Rule::boolean => visitor.visit_bool(parse_bool(&pair)),
- Rule::string | Rule::identifier => visitor.visit_string(parse_string(pair)?),
- Rule::number => {
- if is_int(pair.as_str()) {
- visitor.visit_i64(parse_integer(&pair)?)
- } else {
- visitor.visit_f64(parse_number(&pair)?)
+ let span = pair.as_span();
+ let mut res = (move || {
+ match pair.as_rule() {
+ Rule::null => visitor.visit_unit(),
+ Rule::boolean => visitor.visit_bool(parse_bool(&pair)),
+ Rule::string | Rule::identifier => visitor.visit_string(parse_string(pair)?),
+ Rule::number => {
+ if is_int(pair.as_str()) {
+ visitor.visit_i64(parse_integer(&pair)?)
+ } else {
+ visitor.visit_f64(parse_number(&pair)?)
+ }
}
+ Rule::array => visitor.visit_seq(Seq::new(pair)),
+ Rule::object => visitor.visit_map(Map::new(pair)),
+ _ => unreachable!(),
}
- Rule::array => visitor.visit_seq(Seq::new(pair)),
- Rule::object => visitor.visit_map(Map::new(pair)),
- _ => unreachable!(),
- }
+ })();
+ error::set_location(&mut res, &span);
+ res
}
fn deserialize_enum<V>(
@@ -74,9 +79,13 @@
where
V: de::Visitor<'de>,
{
- visitor.visit_enum(Enum {
- pair: self.pair.take().unwrap(),
- })
+ let pair = self.pair.take().unwrap();
+ let span = pair.as_span();
+ let mut res = (move || {
+ visitor.visit_enum(Enum { pair })
+ })();
+ error::set_location(&mut res, &span);
+ res
}
// The below will get us the right types, but won't necessarily give
@@ -86,7 +95,12 @@
V: de::Visitor<'de>,
{
let pair = self.pair.take().unwrap();
- visitor.visit_i8(parse_number(&pair)? as i8)
+ let span = pair.as_span();
+ let mut res = (move || {
+ visitor.visit_i8(parse_number(&pair)? as i8)
+ })();
+ error::set_location(&mut res, &span);
+ res
}
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value>
@@ -94,7 +108,12 @@
V: de::Visitor<'de>,
{
let pair = self.pair.take().unwrap();
- visitor.visit_i16(parse_number(&pair)? as i16)
+ let span = pair.as_span();
+ let mut res = (move || {
+ visitor.visit_i16(parse_number(&pair)? as i16)
+ })();
+ error::set_location(&mut res, &span);
+ res
}
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value>
@@ -102,7 +121,12 @@
V: de::Visitor<'de>,
{
let pair = self.pair.take().unwrap();
- visitor.visit_i32(parse_number(&pair)? as i32)
+ let span = pair.as_span();
+ let mut res = (move || {
+ visitor.visit_i32(parse_number(&pair)? as i32)
+ })();
+ error::set_location(&mut res, &span);
+ res
}
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value>
@@ -110,7 +134,12 @@
V: de::Visitor<'de>,
{
let pair = self.pair.take().unwrap();
- visitor.visit_i64(parse_number(&pair)? as i64)
+ let span = pair.as_span();
+ let mut res = (move || {
+ visitor.visit_i64(parse_number(&pair)? as i64)
+ })();
+ error::set_location(&mut res, &span);
+ res
}
fn deserialize_i128<V>(self, visitor: V) -> Result<V::Value>
@@ -118,7 +147,12 @@
V: de::Visitor<'de>,
{
let pair = self.pair.take().unwrap();
- visitor.visit_i128(parse_number(&pair)? as i128)
+ let span = pair.as_span();
+ let mut res = (move || {
+ visitor.visit_i128(parse_number(&pair)? as i128)
+ })();
+ error::set_location(&mut res, &span);
+ res
}
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value>
@@ -126,7 +160,12 @@
V: de::Visitor<'de>,
{
let pair = self.pair.take().unwrap();
- visitor.visit_u8(parse_number(&pair)? as u8)
+ let span = pair.as_span();
+ let mut res = (move || {
+ visitor.visit_u8(parse_number(&pair)? as u8)
+ })();
+ error::set_location(&mut res, &span);
+ res
}
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value>
@@ -134,7 +173,12 @@
V: de::Visitor<'de>,
{
let pair = self.pair.take().unwrap();
- visitor.visit_u16(parse_number(&pair)? as u16)
+ let span = pair.as_span();
+ let mut res = (move || {
+ visitor.visit_u16(parse_number(&pair)? as u16)
+ })();
+ error::set_location(&mut res, &span);
+ res
}
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value>
@@ -142,7 +186,12 @@
V: de::Visitor<'de>,
{
let pair = self.pair.take().unwrap();
- visitor.visit_u32(parse_number(&pair)? as u32)
+ let span = pair.as_span();
+ let mut res = (move || {
+ visitor.visit_u32(parse_number(&pair)? as u32)
+ })();
+ error::set_location(&mut res, &span);
+ res
}
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value>
@@ -150,7 +199,12 @@
V: de::Visitor<'de>,
{
let pair = self.pair.take().unwrap();
- visitor.visit_u64(parse_number(&pair)? as u64)
+ let span = pair.as_span();
+ let mut res = (move || {
+ visitor.visit_u64(parse_number(&pair)? as u64)
+ })();
+ error::set_location(&mut res, &span);
+ res
}
fn deserialize_u128<V>(self, visitor: V) -> Result<V::Value>
@@ -158,7 +212,12 @@
V: de::Visitor<'de>,
{
let pair = self.pair.take().unwrap();
- visitor.visit_u128(parse_number(&pair)? as u128)
+ let span = pair.as_span();
+ let mut res = (move || {
+ visitor.visit_u128(parse_number(&pair)? as u128)
+ })();
+ error::set_location(&mut res, &span);
+ res
}
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value>
@@ -166,7 +225,12 @@
V: de::Visitor<'de>,
{
let pair = self.pair.take().unwrap();
- visitor.visit_f32(parse_number(&pair)? as f32)
+ let span = pair.as_span();
+ let mut res = (move || {
+ visitor.visit_f32(parse_number(&pair)? as f32)
+ })();
+ error::set_location(&mut res, &span);
+ res
}
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value>
@@ -174,7 +238,12 @@
V: de::Visitor<'de>,
{
let pair = self.pair.take().unwrap();
- visitor.visit_f64(parse_number(&pair)?)
+ let span = pair.as_span();
+ let mut res = (move || {
+ visitor.visit_f64(parse_number(&pair)?)
+ })();
+ error::set_location(&mut res, &span);
+ res
}
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
@@ -182,17 +251,27 @@
V: de::Visitor<'de>,
{
let pair = self.pair.take().unwrap();
- match pair.as_rule() {
- Rule::null => visitor.visit_none(),
- _ => visitor.visit_some(&mut Deserializer::from_pair(pair)),
- }
+ let span = pair.as_span();
+ let mut res = (move || {
+ match pair.as_rule() {
+ Rule::null => visitor.visit_none(),
+ _ => visitor.visit_some(&mut Deserializer::from_pair(pair)),
+ }
+ })();
+ error::set_location(&mut res, &span);
+ res
}
fn deserialize_newtype_struct<V>(self, _name: &str, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
- visitor.visit_newtype_struct(self)
+ let span = self.pair.as_ref().unwrap().as_span();
+ let mut res = (move || {
+ visitor.visit_newtype_struct(self)
+ })();
+ error::set_location(&mut res, &span);
+ res
}
forward_to_deserialize_any! {
@@ -210,6 +289,8 @@
}
fn parse_string(pair: Pair<'_, Rule>) -> Result<String> {
+ let span = pair.as_span();
+ let mut res = (move || {
pair.into_inner()
.map(|component| match component.as_rule() {
Rule::char_literal => Ok(String::from(component.as_str())),
@@ -225,6 +306,9 @@
_ => unreachable!(),
})
.collect()
+ })();
+ error::set_location(&mut res, &span);
+ res
}
fn parse_char_escape_sequence(pair: &Pair<'_, Rule>) -> String {
@@ -365,23 +449,28 @@
where
V: de::DeserializeSeed<'de>,
{
- match self.pair.as_rule() {
- Rule::string => {
- let tag = seed.deserialize(&mut Deserializer::from_pair(self.pair))?;
- Ok((tag, Variant { pair: None }))
- }
- Rule::object => {
- let mut pairs = self.pair.into_inner();
-
- if let Some(tag_pair) = pairs.next() {
- let tag = seed.deserialize(&mut Deserializer::from_pair(tag_pair))?;
- Ok((tag, Variant { pair: pairs.next() }))
- } else {
- Err(de::Error::custom("expected a nonempty object"))
+ let span = self.pair.as_span();
+ let mut res = (move || {
+ match self.pair.as_rule() {
+ Rule::string => {
+ let tag = seed.deserialize(&mut Deserializer::from_pair(self.pair))?;
+ Ok((tag, Variant { pair: None }))
}
+ Rule::object => {
+ let mut pairs = self.pair.into_inner();
+
+ if let Some(tag_pair) = pairs.next() {
+ let tag = seed.deserialize(&mut Deserializer::from_pair(tag_pair))?;
+ Ok((tag, Variant { pair: pairs.next() }))
+ } else {
+ Err(de::Error::custom("expected a nonempty object"))
+ }
+ }
+ _ => Err(de::Error::custom("expected a string or an object")),
}
- _ => Err(de::Error::custom("expected a string or an object")),
- }
+ })();
+ error::set_location(&mut res, &span);
+ res
}
}
diff --git a/third_party/src/error.rs b/third_party/src/error.rs
index 4399ea0..4da921c 100644
--- a/third_party/src/error.rs
+++ b/third_party/src/error.rs
@@ -1,3 +1,4 @@
+use pest::Span;
use serde::{de, ser};
use std::fmt::{self, Display};
@@ -6,43 +7,75 @@
/// Alias for a `Result` with error type `json5::Error`
pub type Result<T> = std::result::Result<T, Error>;
+/// One-based line and column at which the error was detected.
+#[derive(Clone, Debug, PartialEq)]
+pub struct Location {
+ /// The one-based line number of the error.
+ pub line: usize,
+ /// The one-based column number of the error.
+ pub column: usize,
+}
+
+impl From<&Span<'_>> for Location {
+ fn from(s: &Span<'_>) -> Self {
+ let (line, column) = s.start_pos().line_col();
+ Self { line, column }
+ }
+}
+
/// A bare bones error type which currently just collapses all the underlying errors in to a single
/// string... This is fine for displaying to the user, but not very useful otherwise. Work to be
/// done here.
#[derive(Clone, Debug, PartialEq)]
pub enum Error {
/// Just shove everything in a single variant for now.
- Message(String),
+ Message {
+ /// The error message.
+ msg: String,
+ /// The location of the error, if applicable.
+ location: Option<Location>
+ },
}
impl From<pest::error::Error<Rule>> for Error {
fn from(err: pest::error::Error<Rule>) -> Self {
- Error::Message(err.to_string())
+ let (line, column) = match err.line_col {
+ pest::error::LineColLocation::Pos((l, c)) => (l, c),
+ pest::error::LineColLocation::Span((l, c), (_, _)) => (l, c),
+ };
+ Error::Message { msg: err.to_string(), location: Some(Location { line, column }) }
}
}
impl ser::Error for Error {
fn custom<T: Display>(msg: T) -> Self {
- Error::Message(msg.to_string())
+ Error::Message { msg: msg.to_string(), location: None }
}
}
impl de::Error for Error {
fn custom<T: Display>(msg: T) -> Self {
- Error::Message(msg.to_string())
+ Error::Message { msg: msg.to_string(), location: None }
}
}
impl Display for Error {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
- formatter.write_str(std::error::Error::description(self))
+ match self {
+ Error::Message { ref msg, .. } => write!(formatter, "{}", msg),
+ }
}
}
-impl std::error::Error for Error {
- fn description(&self) -> &str {
- match *self {
- Error::Message(ref msg) => msg,
+impl std::error::Error for Error {}
+
+/// Adds location information from `span`, if `res` is an error.
+pub fn set_location<T>(res: &mut Result<T>, span: &Span<'_>) {
+ if let Err(ref mut e) = res {
+ let Error::Message { location, .. } = e;
+ if location.is_none() {
+ let (line, column) = span.start_pos().line_col();
+ *location = Some(Location { line, column });
}
}
}
diff --git a/third_party/src/lib.rs b/third_party/src/lib.rs
index 68d3e51..eaac541 100644
--- a/third_party/src/lib.rs
+++ b/third_party/src/lib.rs
@@ -123,5 +123,5 @@
mod ser;
pub use crate::de::from_str;
-pub use crate::error::{Error, Result};
+pub use crate::error::{Error, Location, Result};
pub use crate::ser::to_string;
diff --git a/third_party/tests/common.rs b/third_party/tests/common.rs
index 4ed6e53..9eb1ff2 100644
--- a/third_party/tests/common.rs
+++ b/third_party/tests/common.rs
@@ -1,53 +1,41 @@
-use json5;
-use serde;
+use json5::{Error, Location};
+use matches::assert_matches;
-use std::f64;
-
-#[allow(dead_code)]
+#[allow(unused)]
pub fn deserializes_to<'a, T>(s: &'a str, v: T)
where
T: ::std::fmt::Debug + ::std::cmp::PartialEq + serde::de::Deserialize<'a>,
{
- match json5::from_str::<T>(s) {
- Ok(value) => assert_eq!(value, v),
- Err(err) => panic!(format!("{}", err)),
- }
+ assert_matches!(json5::from_str::<T>(s), Ok(value) if value == v);
}
-#[allow(dead_code)]
+#[allow(unused)]
pub fn deserializes_to_nan_f32<'a>(s: &'a str) {
- match json5::from_str::<f32>(s) {
- Ok(value) => assert!(value.is_nan()),
- Err(err) => panic!(format!("{}", err)),
- }
+ assert_matches!(json5::from_str::<f32>(s), Ok(value) if value.is_nan());
}
-#[allow(dead_code)]
+#[allow(unused)]
pub fn deserializes_to_nan_f64<'a>(s: &'a str) {
- match json5::from_str::<f64>(s) {
- Ok(value) => assert!(value.is_nan()),
- Err(err) => panic!(format!("{}", err)),
- }
+ assert_matches!(json5::from_str::<f64>(s), Ok(value) if value.is_nan());
}
-#[allow(dead_code)]
-pub fn deserializes_with_error<'a, T>(s: &'a str, _: T, error_expected: &'a str)
+#[allow(unused)]
+pub fn deserializes_with_error<'a, T>(s: &'a str, error_expected: Error)
where
T: ::std::fmt::Debug + ::std::cmp::PartialEq + serde::de::Deserialize<'a>,
{
- match json5::from_str::<T>(s) {
- Ok(val) => panic!(format!("error expected!, got {:?}", val)),
- Err(err) => assert_eq!(format!("{}", err), error_expected),
- }
+ assert_matches!(json5::from_str::<T>(s), Err(e) if e == error_expected);
}
-#[allow(dead_code)]
+#[allow(unused)]
pub fn serializes_to<T>(v: T, s: &'static str)
where
T: ::std::fmt::Debug + ::std::cmp::PartialEq + serde::ser::Serialize,
{
- match json5::to_string::<T>(&v) {
- Ok(value) => assert_eq!(value, s),
- Err(err) => panic!(format!("{}", err)),
- }
+ assert_matches!(json5::to_string::<T>(&v), Ok(value) if value == s);
+}
+
+#[allow(unused)]
+pub fn make_error(msg: impl Into<String>, line: usize, column: usize) -> Error {
+ Error::Message { msg: msg.into(), location: Some(Location { line, column }) }
}
diff --git a/third_party/tests/de.rs b/third_party/tests/de.rs
index 086b9fc..0474d8c 100644
--- a/third_party/tests/de.rs
+++ b/third_party/tests/de.rs
@@ -8,12 +8,46 @@
use crate::common::{
deserializes_to, deserializes_to_nan_f32, deserializes_to_nan_f64, deserializes_with_error,
+ make_error,
};
+/// Defines a struct `A` with a `de::Deserializer` implementation that returns an error. Works for
+/// visitors that accept a single value.
+macro_rules! error_struct {
+ ($type:ty, $visit_fn:ident, $deserialize_fn:ident) => {
+ #[derive(Debug, PartialEq)]
+ struct A;
+ impl<'de> de::Deserialize<'de> for A {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ struct Visitor;
+ impl<'de> de::Visitor<'de> for Visitor {
+ type Value = A;
+ fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str("...")
+ }
+ fn $visit_fn<E>(self, _v: $type) -> Result<Self::Value, E>
+ where
+ E: de::Error,
+ {
+ Err(de::Error::custom("oops"))
+ }
+ }
+ deserializer.$deserialize_fn(Visitor)
+ }
+ }
+ };
+}
+
#[test]
fn deserializes_bool() {
deserializes_to("true", true);
deserializes_to("false", false);
+
+ error_struct!(bool, visit_bool, deserialize_bool);
+ deserializes_with_error::<A>("\n true", make_error("oops", 2, 2));
}
#[test]
@@ -38,6 +72,9 @@
deserializes_to("-4.2e1", -x);
deserializes_to("-.42e2", -x);
deserializes_to("-0.42e2", -x);
+
+ error_struct!(i8, visit_i8, deserialize_i8);
+ deserializes_with_error::<A>("\n 42", make_error("oops", 2, 2));
}
#[test]
@@ -55,6 +92,9 @@
deserializes_to("4.2e1", x);
deserializes_to(".42e2", x);
deserializes_to("0.42e2", x);
+
+ error_struct!(u8, visit_u8, deserialize_u8);
+ deserializes_with_error::<A>("\n 42", make_error("oops", 2, 2));
}
#[test]
@@ -79,6 +119,9 @@
deserializes_to("-4.2e1", -x);
deserializes_to("-.42e2", -x);
deserializes_to("-0.42e2", -x);
+
+ error_struct!(i16, visit_i16, deserialize_i16);
+ deserializes_with_error::<A>("\n 42", make_error("oops", 2, 2));
}
#[test]
@@ -96,6 +139,9 @@
deserializes_to("4.2e1", x);
deserializes_to(".42e2", x);
deserializes_to("0.42e2", x);
+
+ error_struct!(u16, visit_u16, deserialize_u16);
+ deserializes_with_error::<A>("\n 42", make_error("oops", 2, 2));
}
#[test]
@@ -120,6 +166,9 @@
deserializes_to("-4.2e1", -x);
deserializes_to("-.42e2", -x);
deserializes_to("-0.42e2", -x);
+
+ error_struct!(i32, visit_i32, deserialize_i32);
+ deserializes_with_error::<A>("\n 42", make_error("oops", 2, 2));
}
#[test]
@@ -137,6 +186,9 @@
deserializes_to("4.2e1", x);
deserializes_to(".42e2", x);
deserializes_to("0.42e2", x);
+
+ error_struct!(u32, visit_u32, deserialize_u32);
+ deserializes_with_error::<A>("\n 42", make_error("oops", 2, 2));
}
#[test]
@@ -161,6 +213,14 @@
deserializes_to("-4.2e1", -x);
deserializes_to("-.42e2", -x);
deserializes_to("-0.42e2", -x);
+
+ error_struct!(i64, visit_i64, deserialize_i64);
+ deserializes_with_error::<A>("\n 42", make_error("oops", 2, 2));
+ let over_i64 = format!("\n {}0", i64::max_value());
+ deserializes_with_error::<serde_json::Value>(
+ over_i64.as_str(),
+ make_error("error parsing integer", 2, 2),
+ );
}
#[test]
@@ -178,6 +238,13 @@
deserializes_to("4.2e1", x);
deserializes_to(".42e2", x);
deserializes_to("0.42e2", x);
+
+ deserializes_to("Infinity", std::f32::INFINITY);
+ deserializes_to("-Infinity", std::f32::NEG_INFINITY);
+ deserializes_to_nan_f32("NaN");
+
+ error_struct!(u64, visit_u64, deserialize_u64);
+ deserializes_with_error::<A>("\n 42", make_error("oops", 2, 2));
}
#[test]
@@ -199,6 +266,9 @@
deserializes_to("Infinity", std::f32::INFINITY);
deserializes_to("-Infinity", std::f32::NEG_INFINITY);
deserializes_to_nan_f32("NaN");
+
+ error_struct!(f32, visit_f32, deserialize_f32);
+ deserializes_with_error::<A>("\n 42", make_error("oops", 2, 2));
}
#[test]
@@ -220,6 +290,10 @@
deserializes_to("Infinity", std::f64::INFINITY);
deserializes_to("-Infinity", std::f64::NEG_INFINITY);
deserializes_to_nan_f64("NaN");
+
+ error_struct!(f64, visit_f64, deserialize_f64);
+ deserializes_with_error::<A>("\n 42", make_error("oops", 2, 2));
+ deserializes_with_error::<f64>("\n 1e309", make_error("error parsing number: too large", 2, 2));
}
#[test]
@@ -234,6 +308,10 @@
deserializes_to(r#""\/""#, '/');
deserializes_to(r#""\b""#, '\u{0008}');
deserializes_to(r#""\f""#, '\u{000c}');
+
+ // `deserialize_char` calls `visit_str`
+ error_struct!(&str, visit_str, deserialize_char);
+ deserializes_with_error::<A>("\n 'x'", make_error("oops", 2, 2));
}
#[test]
@@ -247,6 +325,9 @@
fn deserializes_string() {
deserializes_to("'Hello!'", "Hello!".to_owned());
deserializes_to("\"안녕하세요\"", "안녕하세요".to_owned());
+
+ error_struct!(&str, visit_str, deserialize_string);
+ deserializes_with_error::<A>("\n 'Hello!'", make_error("oops", 2, 2));
}
#[test]
@@ -265,18 +346,71 @@
}
#[test]
-fn deserializes_unit() {
- deserializes_to("null", ());
+fn deserializes_option_error() {
+ #[derive(Debug, PartialEq)]
+ struct A;
+ impl<'de> de::Deserialize<'de> for A {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ struct Visitor;
+ impl<'de> de::Visitor<'de> for Visitor {
+ type Value = A;
+ fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str("...")
+ }
+ fn visit_some<D>(self, _deserializer: D) -> Result<Self::Value, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ Err(de::Error::custom("oops"))
+ }
+ }
+ deserializer.deserialize_option(Visitor)
+ }
+ }
+ deserializes_with_error::<A>("\n 42", make_error("oops", 2, 2));
}
#[test]
-fn deserializes_unit_struct() {
- #[derive(Deserialize, PartialEq, Debug)]
+fn deserializes_unit() {
+ deserializes_to("null", ());
+
+ #[derive(Deserialize, Debug, PartialEq)]
struct A;
deserializes_to("null", A);
}
#[test]
+fn deserializes_unit_error() {
+ #[derive(Debug, PartialEq)]
+ struct A;
+ impl<'de> de::Deserialize<'de> for A {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ struct Visitor;
+ impl<'de> de::Visitor<'de> for Visitor {
+ type Value = A;
+ fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str("...")
+ }
+ fn visit_unit<E>(self) -> Result<Self::Value, E>
+ where
+ E: de::Error,
+ {
+ Err(de::Error::custom("oops"))
+ }
+ }
+ deserializer.deserialize_unit(Visitor)
+ }
+ }
+ deserializes_with_error::<A>("\n null", make_error("oops", 2, 2));
+}
+
+#[test]
fn deserializes_newtype_struct() {
#[derive(Deserialize, PartialEq, Debug)]
struct A(i32);
@@ -289,6 +423,34 @@
}
#[test]
+fn deserializes_newtype_struct_error() {
+ #[derive(Debug, PartialEq)]
+ struct A;
+ impl<'de> de::Deserialize<'de> for A {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ struct Visitor;
+ impl<'de> de::Visitor<'de> for Visitor {
+ type Value = A;
+ fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str("...")
+ }
+ fn visit_newtype_struct<D>(self, _deserializer: D) -> Result<Self::Value, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ Err(de::Error::custom("oops"))
+ }
+ }
+ deserializer.deserialize_newtype_struct("A", Visitor)
+ }
+ }
+ deserializes_with_error::<A>("\n 42", make_error("oops", 2, 2));
+}
+
+#[test]
fn deserializes_seq() {
#[derive(Deserialize, PartialEq, Debug)]
#[serde(untagged)]
@@ -323,12 +485,12 @@
type Value = Size;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.write_str("array")
+ f.write_str("...")
}
fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
where
- A: serde::de::SeqAccess<'de>,
+ A: de::SeqAccess<'de>,
{
Ok(Size(seq.size_hint().unwrap()))
}
@@ -343,6 +505,34 @@
}
#[test]
+fn deserializes_seq_error() {
+ #[derive(Debug, PartialEq)]
+ struct A;
+ impl<'de> de::Deserialize<'de> for A {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ struct Visitor;
+ impl<'de> de::Visitor<'de> for Visitor {
+ type Value = A;
+ fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str("...")
+ }
+ fn visit_seq<A>(self, _a: A) -> Result<Self::Value, A::Error>
+ where
+ A: de::SeqAccess<'de>,
+ {
+ Err(de::Error::custom("oops"))
+ }
+ }
+ deserializer.deserialize_seq(Visitor)
+ }
+ }
+ deserializes_with_error::<A>("\n [ true ]", make_error("oops", 2, 2));
+}
+
+#[test]
fn deserializes_tuple() {
deserializes_to("[1, 2, 3]", (1, 2, 3));
}
@@ -360,6 +550,41 @@
}
#[test]
+fn deserializes_tuple_error() {
+ #[derive(Debug, PartialEq)]
+ struct A;
+ impl<'de> de::Deserialize<'de> for A {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ struct Visitor;
+ impl<'de> de::Visitor<'de> for Visitor {
+ type Value = A;
+ fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str("...")
+ }
+ fn visit_seq<A>(self, _a: A) -> Result<Self::Value, A::Error>
+ where
+ A: de::SeqAccess<'de>,
+ {
+ Err(de::Error::custom("oops"))
+ }
+ }
+ deserializer.deserialize_tuple(2, Visitor)
+ }
+ }
+ deserializes_with_error::<A>("\n [1, 2]", make_error("oops", 2, 2));
+
+ #[derive(Deserialize, Debug, PartialEq)]
+ struct B(i32, f64);
+ deserializes_with_error::<B>(
+ "\n [1]",
+ make_error("invalid length 1, expected tuple struct B with 2 elements", 2, 2),
+ );
+}
+
+#[test]
fn deserializes_map() {
let mut m = HashMap::new();
m.insert("a".to_owned(), 1);
@@ -373,6 +598,7 @@
fn deserializes_map_size_hint() {
#[derive(Debug, PartialEq)]
struct Size(usize);
+
impl<'de> de::Deserialize<'de> for Size {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
@@ -383,12 +609,12 @@
type Value = Size;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.write_str("array")
+ f.write_str("...")
}
fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
where
- A: serde::de::MapAccess<'de>,
+ A: de::MapAccess<'de>,
{
Ok(Size(map.size_hint().unwrap()))
}
@@ -403,6 +629,35 @@
}
#[test]
+fn deserializes_map_error() {
+ #[derive(Debug, PartialEq)]
+ struct A {}
+ impl<'de> de::Deserialize<'de> for A {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ struct Visitor;
+ impl<'de> de::Visitor<'de> for Visitor {
+ type Value = A;
+ fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str("...")
+ }
+ fn visit_map<A>(self, _a: A) -> Result<Self::Value, A::Error>
+ where
+ A: de::MapAccess<'de>,
+ {
+ Err(de::Error::custom("oops"))
+ }
+ }
+ deserializer.deserialize_map(Visitor)
+ }
+ }
+
+ deserializes_with_error::<A>("\n { 'a': true }", make_error("oops", 2, 2));
+}
+
+#[test]
fn deserializes_struct() {
#[derive(Deserialize, PartialEq, Debug)]
struct S {
@@ -415,6 +670,17 @@
}
#[test]
+fn deserializes_struct_error() {
+ #[derive(Deserialize, PartialEq, Debug)]
+ struct S {
+ a: i32,
+ b: i32,
+ c: i32,
+ }
+ deserializes_with_error::<S>("\n { a: 1, 'b': 2 }", make_error("missing field `c`", 2, 2));
+}
+
+#[test]
fn deserializes_enum() {
#[derive(Deserialize, PartialEq, Debug)]
enum E {
@@ -435,7 +701,7 @@
}
#[test]
-fn deserializes_enum_with_error() {
+fn deserializes_enum_error() {
#[derive(Deserialize, PartialEq, Debug)]
enum E {
A {},
@@ -447,8 +713,12 @@
e: E,
}
- deserializes_with_error("{ e: 'A' }", S { e: E::A {} }, "expected an object");
- deserializes_with_error("{ e: 'B' }", S { e: E::B() }, "expected an array");
+ deserializes_with_error::<S>("{ e: 'A' }", make_error("expected an object", 1, 6));
+ deserializes_with_error::<S>("{ e: 'B' }", make_error("expected an array", 1, 6));
+ deserializes_with_error::<E>(
+ "\n 'C'",
+ make_error("unknown variant `C`, expected `A` or `B`", 2, 2),
+ );
}
#[test]
@@ -478,22 +748,19 @@
}
#[test]
-fn deserialize_error_messages() {
+fn deserializes_parse_error() {
+ let parse_err_str = r#" --> 1:2
+ |
+1 | {
+ | ^---
+ |
+ = expected identifier or string"#;
#[derive(Deserialize, PartialEq, Debug)]
- enum E {
- A,
- }
- deserializes_with_error("'B'", E::A, "unknown variant `B`, expected `A`");
+ struct A;
+ deserializes_with_error::<A>("{", make_error(parse_err_str, 1, 2));
- deserializes_with_error("0xffffffffff", 42, "error parsing hex");
-
- let mut over_i64 = i64::max_value().to_string();
- over_i64.push_str("0");
- deserializes_with_error(
- over_i64.as_str(),
- serde_json::json!(42),
- "error parsing integer",
+ deserializes_with_error::<bool>(
+ "\n 42",
+ make_error("invalid type: integer `42`, expected a boolean", 2, 2),
);
-
- deserializes_with_error("1e309", 42, "error parsing number: too large");
}