Merge pull request #314 from zertosh/add-line-and-col-to-errors
Add line and column to all Errors
This commit is contained in:
commit
7e8b40b025
208
src/de.rs
208
src/de.rs
|
@ -29,7 +29,7 @@ where
|
||||||
{
|
{
|
||||||
match str::from_utf8(bytes) {
|
match str::from_utf8(bytes) {
|
||||||
Ok(s) => from_str(s),
|
Ok(s) => from_str(s),
|
||||||
Err(e) => Err(Error::custom(e.to_string())),
|
Err(e) => Err(Error::custom(None, e.to_string())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,6 +87,7 @@ struct ErrorInner {
|
||||||
kind: ErrorKind,
|
kind: ErrorKind,
|
||||||
line: Option<usize>,
|
line: Option<usize>,
|
||||||
col: usize,
|
col: usize,
|
||||||
|
at: Option<usize>,
|
||||||
message: String,
|
message: String,
|
||||||
key: Vec<String>,
|
key: Vec<String>,
|
||||||
}
|
}
|
||||||
|
@ -209,7 +210,7 @@ impl<'de, 'b> de::Deserializer<'de> for &'b mut Deserializer<'de> {
|
||||||
{
|
{
|
||||||
let mut tables = self.tables()?;
|
let mut tables = self.tables()?;
|
||||||
|
|
||||||
visitor.visit_map(MapVisitor {
|
let res = visitor.visit_map(MapVisitor {
|
||||||
values: Vec::new().into_iter(),
|
values: Vec::new().into_iter(),
|
||||||
next_value: None,
|
next_value: None,
|
||||||
depth: 0,
|
depth: 0,
|
||||||
|
@ -219,6 +220,17 @@ impl<'de, 'b> de::Deserializer<'de> for &'b mut Deserializer<'de> {
|
||||||
tables: &mut tables,
|
tables: &mut tables,
|
||||||
array: false,
|
array: false,
|
||||||
de: self,
|
de: self,
|
||||||
|
});
|
||||||
|
res.map_err(|mut err| {
|
||||||
|
// Errors originating from this library (toml), have an offset
|
||||||
|
// attached to them already. Other errors, like those originating
|
||||||
|
// from serde (like "missing field") or from a custom deserializer,
|
||||||
|
// do not have offsets on them. Here, we do a best guess at their
|
||||||
|
// location, by attributing them to the "current table" (the last
|
||||||
|
// item in `tables`).
|
||||||
|
err.fix_offset(|| tables.last().map(|table| table.at));
|
||||||
|
err.fix_linecol(|at| self.to_linecol(at));
|
||||||
|
err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,14 +249,17 @@ impl<'de, 'b> de::Deserializer<'de> for &'b mut Deserializer<'de> {
|
||||||
E::String(val) => visitor.visit_enum(val.into_deserializer()),
|
E::String(val) => visitor.visit_enum(val.into_deserializer()),
|
||||||
E::InlineTable(values) => {
|
E::InlineTable(values) => {
|
||||||
if values.len() != 1 {
|
if values.len() != 1 {
|
||||||
Err(Error::from_kind(ErrorKind::Wanted {
|
Err(Error::from_kind(
|
||||||
expected: "exactly 1 element",
|
Some(value.start),
|
||||||
found: if values.is_empty() {
|
ErrorKind::Wanted {
|
||||||
"zero elements"
|
expected: "exactly 1 element",
|
||||||
} else {
|
found: if values.is_empty() {
|
||||||
"more than 1 element"
|
"zero elements"
|
||||||
|
} else {
|
||||||
|
"more than 1 element"
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}))
|
))
|
||||||
} else {
|
} else {
|
||||||
visitor.visit_enum(InlineTableDeserializer {
|
visitor.visit_enum(InlineTableDeserializer {
|
||||||
values: values.into_iter(),
|
values: values.into_iter(),
|
||||||
|
@ -256,10 +271,13 @@ impl<'de, 'b> de::Deserializer<'de> for &'b mut Deserializer<'de> {
|
||||||
name: name.expect("Expected table header to be passed."),
|
name: name.expect("Expected table header to be passed."),
|
||||||
value: value,
|
value: value,
|
||||||
}),
|
}),
|
||||||
e @ _ => Err(Error::from_kind(ErrorKind::Wanted {
|
e @ _ => Err(Error::from_kind(
|
||||||
expected: "string or table",
|
Some(value.start),
|
||||||
found: e.type_name(),
|
ErrorKind::Wanted {
|
||||||
})),
|
expected: "string or table",
|
||||||
|
found: e.type_name(),
|
||||||
|
},
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -556,7 +574,8 @@ impl<'de> de::Deserializer<'de> for ValueDeserializer<'de> {
|
||||||
where
|
where
|
||||||
V: de::Visitor<'de>,
|
V: de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
match self.value.e {
|
let start = self.value.start;
|
||||||
|
let res = match self.value.e {
|
||||||
E::Integer(i) => visitor.visit_i64(i),
|
E::Integer(i) => visitor.visit_i64(i),
|
||||||
E::Boolean(b) => visitor.visit_bool(b),
|
E::Boolean(b) => visitor.visit_bool(b),
|
||||||
E::Float(f) => visitor.visit_f64(f),
|
E::Float(f) => visitor.visit_f64(f),
|
||||||
|
@ -578,7 +597,12 @@ impl<'de> de::Deserializer<'de> for ValueDeserializer<'de> {
|
||||||
next_value: None,
|
next_value: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
res.map_err(|mut err| {
|
||||||
|
// Attribute the error to whatever value returned the error.
|
||||||
|
err.fix_offset(|| Some(start));
|
||||||
|
err
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_struct<V>(
|
fn deserialize_struct<V>(
|
||||||
|
@ -615,13 +639,16 @@ impl<'de> de::Deserializer<'de> for ValueDeserializer<'de> {
|
||||||
.collect::<Vec<Cow<'de, str>>>();
|
.collect::<Vec<Cow<'de, str>>>();
|
||||||
|
|
||||||
if !extra_fields.is_empty() {
|
if !extra_fields.is_empty() {
|
||||||
return Err(Error::from_kind(ErrorKind::UnexpectedKeys {
|
return Err(Error::from_kind(
|
||||||
keys: extra_fields
|
Some(self.value.start),
|
||||||
.iter()
|
ErrorKind::UnexpectedKeys {
|
||||||
.map(|k| k.to_string())
|
keys: extra_fields
|
||||||
.collect::<Vec<_>>(),
|
.iter()
|
||||||
available: fields,
|
.map(|k| k.to_string())
|
||||||
}));
|
.collect::<Vec<_>>(),
|
||||||
|
available: fields,
|
||||||
|
},
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -664,14 +691,17 @@ impl<'de> de::Deserializer<'de> for ValueDeserializer<'de> {
|
||||||
E::String(val) => visitor.visit_enum(val.into_deserializer()),
|
E::String(val) => visitor.visit_enum(val.into_deserializer()),
|
||||||
E::InlineTable(values) => {
|
E::InlineTable(values) => {
|
||||||
if values.len() != 1 {
|
if values.len() != 1 {
|
||||||
Err(Error::from_kind(ErrorKind::Wanted {
|
Err(Error::from_kind(
|
||||||
expected: "exactly 1 element",
|
Some(self.value.start),
|
||||||
found: if values.is_empty() {
|
ErrorKind::Wanted {
|
||||||
"zero elements"
|
expected: "exactly 1 element",
|
||||||
} else {
|
found: if values.is_empty() {
|
||||||
"more than 1 element"
|
"zero elements"
|
||||||
|
} else {
|
||||||
|
"more than 1 element"
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}))
|
))
|
||||||
} else {
|
} else {
|
||||||
visitor.visit_enum(InlineTableDeserializer {
|
visitor.visit_enum(InlineTableDeserializer {
|
||||||
values: values.into_iter(),
|
values: values.into_iter(),
|
||||||
|
@ -679,10 +709,13 @@ impl<'de> de::Deserializer<'de> for ValueDeserializer<'de> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
e @ _ => Err(Error::from_kind(ErrorKind::Wanted {
|
e @ _ => Err(Error::from_kind(
|
||||||
expected: "string or inline table",
|
Some(self.value.start),
|
||||||
found: e.type_name(),
|
ErrorKind::Wanted {
|
||||||
})),
|
expected: "string or inline table",
|
||||||
|
found: e.type_name(),
|
||||||
|
},
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -860,10 +893,13 @@ impl<'de> de::EnumAccess<'de> for InlineTableDeserializer<'de> {
|
||||||
let (key, value) = match self.values.next() {
|
let (key, value) = match self.values.next() {
|
||||||
Some(pair) => pair,
|
Some(pair) => pair,
|
||||||
None => {
|
None => {
|
||||||
return Err(Error::from_kind(ErrorKind::Wanted {
|
return Err(Error::from_kind(
|
||||||
expected: "table with exactly 1 entry",
|
None, // FIXME: How do we get an offset here?
|
||||||
found: "empty table",
|
ErrorKind::Wanted {
|
||||||
}))
|
expected: "table with exactly 1 entry",
|
||||||
|
found: "empty table",
|
||||||
|
},
|
||||||
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -886,13 +922,19 @@ impl<'de> de::VariantAccess<'de> for TableEnumDeserializer<'de> {
|
||||||
if values.len() == 0 {
|
if values.len() == 0 {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::from_kind(ErrorKind::ExpectedEmptyTable))
|
Err(Error::from_kind(
|
||||||
|
Some(self.value.start),
|
||||||
|
ErrorKind::ExpectedEmptyTable,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
e @ _ => Err(Error::from_kind(ErrorKind::Wanted {
|
e @ _ => Err(Error::from_kind(
|
||||||
expected: "table",
|
Some(self.value.start),
|
||||||
found: e.type_name(),
|
ErrorKind::Wanted {
|
||||||
})),
|
expected: "table",
|
||||||
|
found: e.type_name(),
|
||||||
|
},
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -914,10 +956,13 @@ impl<'de> de::VariantAccess<'de> for TableEnumDeserializer<'de> {
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(index, (key, value))| match key.parse::<usize>() {
|
.map(|(index, (key, value))| match key.parse::<usize>() {
|
||||||
Ok(key_index) if key_index == index => Ok(value),
|
Ok(key_index) if key_index == index => Ok(value),
|
||||||
Ok(_) | Err(_) => Err(Error::from_kind(ErrorKind::ExpectedTupleIndex {
|
Ok(_) | Err(_) => Err(Error::from_kind(
|
||||||
expected: index,
|
Some(value.start),
|
||||||
found: key.to_string(),
|
ErrorKind::ExpectedTupleIndex {
|
||||||
})),
|
expected: index,
|
||||||
|
found: key.to_string(),
|
||||||
|
},
|
||||||
|
)),
|
||||||
})
|
})
|
||||||
// Fold all values into a `Vec`, or return the first error.
|
// Fold all values into a `Vec`, or return the first error.
|
||||||
.fold(Ok(Vec::with_capacity(len)), |result, value_result| {
|
.fold(Ok(Vec::with_capacity(len)), |result, value_result| {
|
||||||
|
@ -941,13 +986,19 @@ impl<'de> de::VariantAccess<'de> for TableEnumDeserializer<'de> {
|
||||||
visitor,
|
visitor,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Err(Error::from_kind(ErrorKind::ExpectedTuple(len)))
|
Err(Error::from_kind(
|
||||||
|
Some(self.value.start),
|
||||||
|
ErrorKind::ExpectedTuple(len),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
e @ _ => Err(Error::from_kind(ErrorKind::Wanted {
|
e @ _ => Err(Error::from_kind(
|
||||||
expected: "table",
|
Some(self.value.start),
|
||||||
found: e.type_name(),
|
ErrorKind::Wanted {
|
||||||
})),
|
expected: "table",
|
||||||
|
found: e.type_name(),
|
||||||
|
},
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1195,17 +1246,20 @@ impl<'a> Deserializer<'a> {
|
||||||
/// structures (tuple, newtype, struct) must be represented as a table.
|
/// structures (tuple, newtype, struct) must be represented as a table.
|
||||||
fn string_or_table(&mut self) -> Result<(Value<'a>, Option<Cow<'a, str>>), Error> {
|
fn string_or_table(&mut self) -> Result<(Value<'a>, Option<Cow<'a, str>>), Error> {
|
||||||
match self.peek()? {
|
match self.peek()? {
|
||||||
Some((_, Token::LeftBracket)) => {
|
Some((span, Token::LeftBracket)) => {
|
||||||
let tables = self.tables()?;
|
let tables = self.tables()?;
|
||||||
if tables.len() != 1 {
|
if tables.len() != 1 {
|
||||||
return Err(Error::from_kind(ErrorKind::Wanted {
|
return Err(Error::from_kind(
|
||||||
expected: "exactly 1 table",
|
Some(span.start),
|
||||||
found: if tables.is_empty() {
|
ErrorKind::Wanted {
|
||||||
"zero tables"
|
expected: "exactly 1 table",
|
||||||
} else {
|
found: if tables.is_empty() {
|
||||||
"more than 1 table"
|
"zero tables"
|
||||||
|
} else {
|
||||||
|
"more than 1 table"
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let table = tables
|
let table = tables
|
||||||
|
@ -1710,10 +1764,8 @@ impl<'a> Deserializer<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn error(&self, at: usize, kind: ErrorKind) -> Error {
|
fn error(&self, at: usize, kind: ErrorKind) -> Error {
|
||||||
let mut err = Error::from_kind(kind);
|
let mut err = Error::from_kind(Some(at), kind);
|
||||||
let (line, col) = self.to_linecol(at);
|
err.fix_linecol(|at| self.to_linecol(at));
|
||||||
err.inner.line = Some(line);
|
|
||||||
err.inner.col = col;
|
|
||||||
err
|
err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1740,24 +1792,26 @@ impl Error {
|
||||||
self.inner.line.map(|line| (line, self.inner.col))
|
self.inner.line.map(|line| (line, self.inner.col))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_kind(kind: ErrorKind) -> Error {
|
fn from_kind(at: Option<usize>, kind: ErrorKind) -> Error {
|
||||||
Error {
|
Error {
|
||||||
inner: Box::new(ErrorInner {
|
inner: Box::new(ErrorInner {
|
||||||
kind: kind,
|
kind: kind,
|
||||||
line: None,
|
line: None,
|
||||||
col: 0,
|
col: 0,
|
||||||
|
at,
|
||||||
message: String::new(),
|
message: String::new(),
|
||||||
key: Vec::new(),
|
key: Vec::new(),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn custom(s: String) -> Error {
|
fn custom(at: Option<usize>, s: String) -> Error {
|
||||||
Error {
|
Error {
|
||||||
inner: Box::new(ErrorInner {
|
inner: Box::new(ErrorInner {
|
||||||
kind: ErrorKind::Custom,
|
kind: ErrorKind::Custom,
|
||||||
line: None,
|
line: None,
|
||||||
col: 0,
|
col: 0,
|
||||||
|
at,
|
||||||
message: s,
|
message: s,
|
||||||
key: Vec::new(),
|
key: Vec::new(),
|
||||||
}),
|
}),
|
||||||
|
@ -1770,6 +1824,28 @@ impl Error {
|
||||||
pub fn add_key_context(&mut self, key: &str) {
|
pub fn add_key_context(&mut self, key: &str) {
|
||||||
self.inner.key.insert(0, key.to_string());
|
self.inner.key.insert(0, key.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fix_offset<F>(&mut self, f: F) -> ()
|
||||||
|
where
|
||||||
|
F: FnOnce() -> Option<usize>,
|
||||||
|
{
|
||||||
|
// An existing offset is always better positioned than anything we
|
||||||
|
// might want to add later.
|
||||||
|
if self.inner.at.is_none() {
|
||||||
|
self.inner.at = f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fix_linecol<F>(&mut self, f: F) -> ()
|
||||||
|
where
|
||||||
|
F: FnOnce(usize) -> (usize, usize),
|
||||||
|
{
|
||||||
|
if let Some(at) = self.inner.at {
|
||||||
|
let (line, col) = f(at);
|
||||||
|
self.inner.line = Some(line);
|
||||||
|
self.inner.col = col;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
|
@ -1885,7 +1961,7 @@ impl error::Error for Error {
|
||||||
|
|
||||||
impl de::Error for Error {
|
impl de::Error for Error {
|
||||||
fn custom<T: fmt::Display>(msg: T) -> Error {
|
fn custom<T: fmt::Display>(msg: T) -> Error {
|
||||||
Error::custom(msg.to_string())
|
Error::custom(None, msg.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,6 @@ edition = "2018"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
toml = { path = ".." }
|
toml = { path = ".." }
|
||||||
serde = "1.0"
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
|
|
@ -42,75 +42,93 @@ fn times() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bad_times() {
|
fn bad_times() {
|
||||||
bad!("foo = 199-09-09", "failed to parse datetime for key `foo`");
|
bad!(
|
||||||
bad!("foo = 199709-09", "failed to parse datetime for key `foo`");
|
"foo = 199-09-09",
|
||||||
bad!("foo = 1997-9-09", "failed to parse datetime for key `foo`");
|
"failed to parse datetime for key `foo` at line 1 column 7"
|
||||||
bad!("foo = 1997-09-9", "failed to parse datetime for key `foo`");
|
);
|
||||||
|
bad!(
|
||||||
|
"foo = 199709-09",
|
||||||
|
"failed to parse datetime for key `foo` at line 1 column 7"
|
||||||
|
);
|
||||||
|
bad!(
|
||||||
|
"foo = 1997-9-09",
|
||||||
|
"failed to parse datetime for key `foo` at line 1 column 7"
|
||||||
|
);
|
||||||
|
bad!(
|
||||||
|
"foo = 1997-09-9",
|
||||||
|
"failed to parse datetime for key `foo` at line 1 column 7"
|
||||||
|
);
|
||||||
bad!(
|
bad!(
|
||||||
"foo = 1997-09-0909:09:09",
|
"foo = 1997-09-0909:09:09",
|
||||||
"failed to parse datetime for key `foo`"
|
"failed to parse datetime for key `foo` at line 1 column 7"
|
||||||
);
|
);
|
||||||
bad!(
|
bad!(
|
||||||
"foo = 1997-09-09T09:09:09.",
|
"foo = 1997-09-09T09:09:09.",
|
||||||
"invalid date at line 1 column 7"
|
"invalid date at line 1 column 7"
|
||||||
);
|
);
|
||||||
bad!("foo = T", "failed to parse datetime for key `foo`");
|
bad!(
|
||||||
|
"foo = T",
|
||||||
|
"failed to parse datetime for key `foo` at line 1 column 7"
|
||||||
|
);
|
||||||
bad!(
|
bad!(
|
||||||
"foo = T.",
|
"foo = T.",
|
||||||
"expected newline, found a period at line 1 column 8"
|
"expected newline, found a period at line 1 column 8"
|
||||||
);
|
);
|
||||||
bad!("foo = TZ", "failed to parse datetime for key `foo`");
|
bad!(
|
||||||
|
"foo = TZ",
|
||||||
|
"failed to parse datetime for key `foo` at line 1 column 7"
|
||||||
|
);
|
||||||
bad!(
|
bad!(
|
||||||
"foo = 1997-09-09T09:09:09.09+",
|
"foo = 1997-09-09T09:09:09.09+",
|
||||||
"invalid date at line 1 column 7"
|
"invalid date at line 1 column 7"
|
||||||
);
|
);
|
||||||
bad!(
|
bad!(
|
||||||
"foo = 1997-09-09T09:09:09.09+09",
|
"foo = 1997-09-09T09:09:09.09+09",
|
||||||
"failed to parse datetime for key `foo`"
|
"failed to parse datetime for key `foo` at line 1 column 7"
|
||||||
);
|
);
|
||||||
bad!(
|
bad!(
|
||||||
"foo = 1997-09-09T09:09:09.09+09:9",
|
"foo = 1997-09-09T09:09:09.09+09:9",
|
||||||
"failed to parse datetime for key `foo`"
|
"failed to parse datetime for key `foo` at line 1 column 7"
|
||||||
);
|
);
|
||||||
bad!(
|
bad!(
|
||||||
"foo = 1997-09-09T09:09:09.09+0909",
|
"foo = 1997-09-09T09:09:09.09+0909",
|
||||||
"failed to parse datetime for key `foo`"
|
"failed to parse datetime for key `foo` at line 1 column 7"
|
||||||
);
|
);
|
||||||
bad!(
|
bad!(
|
||||||
"foo = 1997-09-09T09:09:09.09-",
|
"foo = 1997-09-09T09:09:09.09-",
|
||||||
"failed to parse datetime for key `foo`"
|
"failed to parse datetime for key `foo` at line 1 column 7"
|
||||||
);
|
);
|
||||||
bad!(
|
bad!(
|
||||||
"foo = 1997-09-09T09:09:09.09-09",
|
"foo = 1997-09-09T09:09:09.09-09",
|
||||||
"failed to parse datetime for key `foo`"
|
"failed to parse datetime for key `foo` at line 1 column 7"
|
||||||
);
|
);
|
||||||
bad!(
|
bad!(
|
||||||
"foo = 1997-09-09T09:09:09.09-09:9",
|
"foo = 1997-09-09T09:09:09.09-09:9",
|
||||||
"failed to parse datetime for key `foo`"
|
"failed to parse datetime for key `foo` at line 1 column 7"
|
||||||
);
|
);
|
||||||
bad!(
|
bad!(
|
||||||
"foo = 1997-09-09T09:09:09.09-0909",
|
"foo = 1997-09-09T09:09:09.09-0909",
|
||||||
"failed to parse datetime for key `foo`"
|
"failed to parse datetime for key `foo` at line 1 column 7"
|
||||||
);
|
);
|
||||||
|
|
||||||
bad!(
|
bad!(
|
||||||
"foo = 1997-00-09T09:09:09.09Z",
|
"foo = 1997-00-09T09:09:09.09Z",
|
||||||
"failed to parse datetime for key `foo`"
|
"failed to parse datetime for key `foo` at line 1 column 7"
|
||||||
);
|
);
|
||||||
bad!(
|
bad!(
|
||||||
"foo = 1997-09-00T09:09:09.09Z",
|
"foo = 1997-09-00T09:09:09.09Z",
|
||||||
"failed to parse datetime for key `foo`"
|
"failed to parse datetime for key `foo` at line 1 column 7"
|
||||||
);
|
);
|
||||||
bad!(
|
bad!(
|
||||||
"foo = 1997-09-09T30:09:09.09Z",
|
"foo = 1997-09-09T30:09:09.09Z",
|
||||||
"failed to parse datetime for key `foo`"
|
"failed to parse datetime for key `foo` at line 1 column 7"
|
||||||
);
|
);
|
||||||
bad!(
|
bad!(
|
||||||
"foo = 1997-09-09T12:69:09.09Z",
|
"foo = 1997-09-09T12:69:09.09Z",
|
||||||
"failed to parse datetime for key `foo`"
|
"failed to parse datetime for key `foo` at line 1 column 7"
|
||||||
);
|
);
|
||||||
bad!(
|
bad!(
|
||||||
"foo = 1997-09-09T12:09:69.09Z",
|
"foo = 1997-09-09T12:09:69.09Z",
|
||||||
"failed to parse datetime for key `foo`"
|
"failed to parse datetime for key `foo` at line 1 column 7"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
325
test-suite/tests/de-errors.rs
Normal file
325
test-suite/tests/de-errors.rs
Normal file
|
@ -0,0 +1,325 @@
|
||||||
|
extern crate serde;
|
||||||
|
extern crate toml;
|
||||||
|
|
||||||
|
use serde::{de, Deserialize};
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
macro_rules! bad {
|
||||||
|
($toml:expr, $ty:ty, $msg:expr) => {
|
||||||
|
match toml::from_str::<$ty>($toml) {
|
||||||
|
Ok(s) => panic!("parsed to: {:#?}", s),
|
||||||
|
Err(e) => assert_eq!(e.to_string(), $msg),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, PartialEq)]
|
||||||
|
struct Parent<T> {
|
||||||
|
p_a: T,
|
||||||
|
p_b: Vec<Child<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, PartialEq)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
struct Child<T> {
|
||||||
|
c_a: T,
|
||||||
|
c_b: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
enum CasedString {
|
||||||
|
Lowercase(String),
|
||||||
|
Uppercase(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> de::Deserialize<'de> for CasedString {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: de::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
struct CasedStringVisitor;
|
||||||
|
|
||||||
|
impl<'de> de::Visitor<'de> for CasedStringVisitor {
|
||||||
|
type Value = CasedString;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str("a string")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
if s.is_empty() {
|
||||||
|
Err(de::Error::invalid_length(0, &"a non-empty string"))
|
||||||
|
} else if s.chars().all(|x| x.is_ascii_lowercase()) {
|
||||||
|
Ok(CasedString::Lowercase(s.to_string()))
|
||||||
|
} else if s.chars().all(|x| x.is_ascii_uppercase()) {
|
||||||
|
Ok(CasedString::Uppercase(s.to_string()))
|
||||||
|
} else {
|
||||||
|
Err(de::Error::invalid_value(
|
||||||
|
de::Unexpected::Str(s),
|
||||||
|
&"all lowercase or all uppercase",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.deserialize_any(CasedStringVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn custom_errors() {
|
||||||
|
toml::from_str::<Parent<CasedString>>(
|
||||||
|
"
|
||||||
|
p_a = 'a'
|
||||||
|
p_b = [{c_a = 'a', c_b = 'c'}]
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Custom error at p_b value.
|
||||||
|
bad!(
|
||||||
|
"
|
||||||
|
p_a = ''
|
||||||
|
# ^
|
||||||
|
",
|
||||||
|
Parent<CasedString>,
|
||||||
|
"invalid length 0, expected a non-empty string for key `p_a` at line 2 column 19"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Missing field in table.
|
||||||
|
bad!(
|
||||||
|
"
|
||||||
|
p_a = 'a'
|
||||||
|
# ^
|
||||||
|
",
|
||||||
|
Parent<CasedString>,
|
||||||
|
"missing field `p_b` at line 1 column 1"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Invalid type in p_b.
|
||||||
|
bad!(
|
||||||
|
"
|
||||||
|
p_a = 'a'
|
||||||
|
p_b = 1
|
||||||
|
# ^
|
||||||
|
",
|
||||||
|
Parent<CasedString>,
|
||||||
|
"invalid type: integer `1`, expected a sequence for key `p_b` at line 3 column 19"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sub-table in Vec is missing a field.
|
||||||
|
bad!(
|
||||||
|
"
|
||||||
|
p_a = 'a'
|
||||||
|
p_b = [
|
||||||
|
{c_a = 'a'}
|
||||||
|
# ^
|
||||||
|
]
|
||||||
|
",
|
||||||
|
Parent<CasedString>,
|
||||||
|
"missing field `c_b` for key `p_b` at line 4 column 17"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sub-table in Vec has a field with a bad value.
|
||||||
|
bad!(
|
||||||
|
"
|
||||||
|
p_a = 'a'
|
||||||
|
p_b = [
|
||||||
|
{c_a = 'a', c_b = '*'}
|
||||||
|
# ^
|
||||||
|
]
|
||||||
|
",
|
||||||
|
Parent<CasedString>,
|
||||||
|
"invalid value: string \"*\", expected all lowercase or all uppercase for key `p_b` at line 4 column 35"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sub-table in Vec is missing a field.
|
||||||
|
bad!(
|
||||||
|
"
|
||||||
|
p_a = 'a'
|
||||||
|
p_b = [
|
||||||
|
{c_a = 'a', c_b = 'b'},
|
||||||
|
{c_a = 'aa'}
|
||||||
|
# ^
|
||||||
|
]
|
||||||
|
",
|
||||||
|
Parent<CasedString>,
|
||||||
|
"missing field `c_b` for key `p_b` at line 5 column 17"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sub-table in the middle of a Vec is missing a field.
|
||||||
|
bad!(
|
||||||
|
"
|
||||||
|
p_a = 'a'
|
||||||
|
p_b = [
|
||||||
|
{c_a = 'a', c_b = 'b'},
|
||||||
|
{c_a = 'aa'},
|
||||||
|
# ^
|
||||||
|
{c_a = 'aaa', c_b = 'bbb'},
|
||||||
|
]
|
||||||
|
",
|
||||||
|
Parent<CasedString>,
|
||||||
|
"missing field `c_b` for key `p_b` at line 5 column 17"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sub-table in the middle of a Vec has a field with a bad value.
|
||||||
|
bad!(
|
||||||
|
"
|
||||||
|
p_a = 'a'
|
||||||
|
p_b = [
|
||||||
|
{c_a = 'a', c_b = 'b'},
|
||||||
|
{c_a = 'aa', c_b = 1},
|
||||||
|
# ^
|
||||||
|
{c_a = 'aaa', c_b = 'bbb'},
|
||||||
|
]
|
||||||
|
",
|
||||||
|
Parent<CasedString>,
|
||||||
|
"invalid type: integer `1`, expected a string for key `p_b` at line 5 column 36"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sub-table in the middle of a Vec has an extra field.
|
||||||
|
// FIXME: This location could be better.
|
||||||
|
bad!(
|
||||||
|
"
|
||||||
|
p_a = 'a'
|
||||||
|
p_b = [
|
||||||
|
{c_a = 'a', c_b = 'b'},
|
||||||
|
{c_a = 'aa', c_b = 'bb', c_d = 'd'},
|
||||||
|
# ^
|
||||||
|
{c_a = 'aaa', c_b = 'bbb'},
|
||||||
|
{c_a = 'aaaa', c_b = 'bbbb'},
|
||||||
|
]
|
||||||
|
",
|
||||||
|
Parent<CasedString>,
|
||||||
|
"unknown field `c_d`, expected `c_a` or `c_b` for key `p_b` at line 5 column 17"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sub-table in the middle of a Vec is missing a field.
|
||||||
|
// FIXME: This location is pretty off.
|
||||||
|
bad!(
|
||||||
|
"
|
||||||
|
p_a = 'a'
|
||||||
|
[[p_b]]
|
||||||
|
c_a = 'a'
|
||||||
|
c_b = 'b'
|
||||||
|
[[p_b]]
|
||||||
|
c_a = 'aa'
|
||||||
|
# c_b = 'bb' # <- missing field
|
||||||
|
[[p_b]]
|
||||||
|
c_a = 'aaa'
|
||||||
|
c_b = 'bbb'
|
||||||
|
[[p_b]]
|
||||||
|
# ^
|
||||||
|
c_a = 'aaaa'
|
||||||
|
c_b = 'bbbb'
|
||||||
|
",
|
||||||
|
Parent<CasedString>,
|
||||||
|
"missing field `c_b` for key `p_b` at line 12 column 13"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sub-table in the middle of a Vec has a field with a bad value.
|
||||||
|
bad!(
|
||||||
|
"
|
||||||
|
p_a = 'a'
|
||||||
|
[[p_b]]
|
||||||
|
c_a = 'a'
|
||||||
|
c_b = 'b'
|
||||||
|
[[p_b]]
|
||||||
|
c_a = 'aa'
|
||||||
|
c_b = '*'
|
||||||
|
# ^
|
||||||
|
[[p_b]]
|
||||||
|
c_a = 'aaa'
|
||||||
|
c_b = 'bbb'
|
||||||
|
",
|
||||||
|
Parent<CasedString>,
|
||||||
|
"invalid value: string \"*\", expected all lowercase or all uppercase for key `p_b.c_b` at line 8 column 19"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sub-table in the middle of a Vec has an extra field.
|
||||||
|
// FIXME: This location is pretty off.
|
||||||
|
bad!(
|
||||||
|
"
|
||||||
|
p_a = 'a'
|
||||||
|
[[p_b]]
|
||||||
|
c_a = 'a'
|
||||||
|
c_b = 'b'
|
||||||
|
[[p_b]]
|
||||||
|
c_a = 'aa'
|
||||||
|
c_d = 'dd' # unknown field
|
||||||
|
[[p_b]]
|
||||||
|
c_a = 'aaa'
|
||||||
|
c_b = 'bbb'
|
||||||
|
[[p_b]]
|
||||||
|
# ^
|
||||||
|
c_a = 'aaaa'
|
||||||
|
c_b = 'bbbb'
|
||||||
|
",
|
||||||
|
Parent<CasedString>,
|
||||||
|
"unknown field `c_d`, expected `c_a` or `c_b` for key `p_b` at line 12 column 13"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serde_derive_deserialize_errors() {
|
||||||
|
bad!(
|
||||||
|
"
|
||||||
|
p_a = ''
|
||||||
|
# ^
|
||||||
|
",
|
||||||
|
Parent<String>,
|
||||||
|
"missing field `p_b` at line 1 column 1"
|
||||||
|
);
|
||||||
|
|
||||||
|
bad!(
|
||||||
|
"
|
||||||
|
p_a = ''
|
||||||
|
p_b = [
|
||||||
|
{c_a = ''}
|
||||||
|
# ^
|
||||||
|
]
|
||||||
|
",
|
||||||
|
Parent<String>,
|
||||||
|
"missing field `c_b` for key `p_b` at line 4 column 17"
|
||||||
|
);
|
||||||
|
|
||||||
|
bad!(
|
||||||
|
"
|
||||||
|
p_a = ''
|
||||||
|
p_b = [
|
||||||
|
{c_a = '', c_b = 1}
|
||||||
|
# ^
|
||||||
|
]
|
||||||
|
",
|
||||||
|
Parent<String>,
|
||||||
|
"invalid type: integer `1`, expected a string for key `p_b` at line 4 column 34"
|
||||||
|
);
|
||||||
|
|
||||||
|
// FIXME: This location could be better.
|
||||||
|
bad!(
|
||||||
|
"
|
||||||
|
p_a = ''
|
||||||
|
p_b = [
|
||||||
|
{c_a = '', c_b = '', c_d = ''},
|
||||||
|
# ^
|
||||||
|
]
|
||||||
|
",
|
||||||
|
Parent<String>,
|
||||||
|
"unknown field `c_d`, expected `c_a` or `c_b` for key `p_b` at line 4 column 17"
|
||||||
|
);
|
||||||
|
|
||||||
|
bad!(
|
||||||
|
"
|
||||||
|
p_a = 'a'
|
||||||
|
p_b = [
|
||||||
|
{c_a = '', c_b = 1, c_d = ''},
|
||||||
|
# ^
|
||||||
|
]
|
||||||
|
",
|
||||||
|
Parent<String>,
|
||||||
|
"invalid type: integer `1`, expected a string for key `p_b` at line 4 column 34"
|
||||||
|
);
|
||||||
|
}
|
|
@ -27,14 +27,17 @@ fn bad() {
|
||||||
);
|
);
|
||||||
|
|
||||||
bad!("a = -0x1", "invalid number at line 1 column 5");
|
bad!("a = -0x1", "invalid number at line 1 column 5");
|
||||||
bad!("a = 0x-1", "failed to parse datetime for key `a`");
|
bad!(
|
||||||
|
"a = 0x-1",
|
||||||
|
"failed to parse datetime for key `a` at line 1 column 5"
|
||||||
|
);
|
||||||
|
|
||||||
// Dotted keys.
|
// Dotted keys.
|
||||||
bad!(
|
bad!(
|
||||||
"a.b.c = 1
|
"a.b.c = 1
|
||||||
a.b = 2
|
a.b = 2
|
||||||
",
|
",
|
||||||
"duplicate key: `b` for key `a`"
|
"duplicate key: `b` for key `a` at line 1 column 9"
|
||||||
);
|
);
|
||||||
bad!(
|
bad!(
|
||||||
"a = 1
|
"a = 1
|
||||||
|
|
|
@ -32,7 +32,7 @@ test!(
|
||||||
test!(
|
test!(
|
||||||
datetime_malformed_no_leads,
|
datetime_malformed_no_leads,
|
||||||
include_str!("invalid/datetime-malformed-no-leads.toml"),
|
include_str!("invalid/datetime-malformed-no-leads.toml"),
|
||||||
"failed to parse datetime for key `no-leads`"
|
"failed to parse datetime for key `no-leads` at line 1 column 12"
|
||||||
);
|
);
|
||||||
test!(
|
test!(
|
||||||
datetime_malformed_no_secs,
|
datetime_malformed_no_secs,
|
||||||
|
@ -42,22 +42,22 @@ test!(
|
||||||
test!(
|
test!(
|
||||||
datetime_malformed_no_t,
|
datetime_malformed_no_t,
|
||||||
include_str!("invalid/datetime-malformed-no-t.toml"),
|
include_str!("invalid/datetime-malformed-no-t.toml"),
|
||||||
"failed to parse datetime for key `no-t`"
|
"failed to parse datetime for key `no-t` at line 1 column 8"
|
||||||
);
|
);
|
||||||
test!(
|
test!(
|
||||||
datetime_malformed_with_milli,
|
datetime_malformed_with_milli,
|
||||||
include_str!("invalid/datetime-malformed-with-milli.toml"),
|
include_str!("invalid/datetime-malformed-with-milli.toml"),
|
||||||
"failed to parse datetime for key `with-milli`"
|
"failed to parse datetime for key `with-milli` at line 1 column 14"
|
||||||
);
|
);
|
||||||
test!(
|
test!(
|
||||||
duplicate_key_table,
|
duplicate_key_table,
|
||||||
include_str!("invalid/duplicate-key-table.toml"),
|
include_str!("invalid/duplicate-key-table.toml"),
|
||||||
"duplicate key: `type` for key `fruit`"
|
"duplicate key: `type` for key `fruit` at line 4 column 1"
|
||||||
);
|
);
|
||||||
test!(
|
test!(
|
||||||
duplicate_keys,
|
duplicate_keys,
|
||||||
include_str!("invalid/duplicate-keys.toml"),
|
include_str!("invalid/duplicate-keys.toml"),
|
||||||
"duplicate key: `dupe`"
|
"duplicate key: `dupe` at line 1 column 1"
|
||||||
);
|
);
|
||||||
test!(
|
test!(
|
||||||
duplicate_table,
|
duplicate_table,
|
||||||
|
|
|
@ -453,7 +453,10 @@ fn inline_tables() {
|
||||||
"a = {,}",
|
"a = {,}",
|
||||||
"expected a table key, found a comma at line 1 column 6"
|
"expected a table key, found a comma at line 1 column 6"
|
||||||
);
|
);
|
||||||
bad!("a = {a=1,a=1}", "duplicate key: `a` for key `a`");
|
bad!(
|
||||||
|
"a = {a=1,a=1}",
|
||||||
|
"duplicate key: `a` for key `a` at line 1 column 5"
|
||||||
|
);
|
||||||
bad!(
|
bad!(
|
||||||
"a = {\n}",
|
"a = {\n}",
|
||||||
"expected a table key, found a newline at line 1 column 6"
|
"expected a table key, found a newline at line 1 column 6"
|
||||||
|
@ -533,9 +536,15 @@ fn booleans() {
|
||||||
let table = "foo = false".parse::<Value>().unwrap();
|
let table = "foo = false".parse::<Value>().unwrap();
|
||||||
assert_eq!(table["foo"].as_bool(), Some(false));
|
assert_eq!(table["foo"].as_bool(), Some(false));
|
||||||
|
|
||||||
bad!("foo = true2", "failed to parse datetime for key `foo`");
|
bad!(
|
||||||
|
"foo = true2",
|
||||||
|
"failed to parse datetime for key `foo` at line 1 column 7"
|
||||||
|
);
|
||||||
bad!("foo = false2", "invalid number at line 1 column 7");
|
bad!("foo = false2", "invalid number at line 1 column 7");
|
||||||
bad!("foo = t1", "failed to parse datetime for key `foo`");
|
bad!(
|
||||||
|
"foo = t1",
|
||||||
|
"failed to parse datetime for key `foo` at line 1 column 7"
|
||||||
|
);
|
||||||
bad!("foo = f2", "invalid number at line 1 column 7");
|
bad!("foo = f2", "invalid number at line 1 column 7");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -547,28 +556,28 @@ fn bad_nesting() {
|
||||||
[[a]]
|
[[a]]
|
||||||
b = 5
|
b = 5
|
||||||
",
|
",
|
||||||
"duplicate key: `a`"
|
"duplicate key: `a` at line 3 column 9"
|
||||||
);
|
);
|
||||||
bad!(
|
bad!(
|
||||||
"
|
"
|
||||||
a = 1
|
a = 1
|
||||||
[a.b]
|
[a.b]
|
||||||
",
|
",
|
||||||
"duplicate key: `a`"
|
"duplicate key: `a` at line 3 column 9"
|
||||||
);
|
);
|
||||||
bad!(
|
bad!(
|
||||||
"
|
"
|
||||||
a = []
|
a = []
|
||||||
[a.b]
|
[a.b]
|
||||||
",
|
",
|
||||||
"duplicate key: `a`"
|
"duplicate key: `a` at line 3 column 9"
|
||||||
);
|
);
|
||||||
bad!(
|
bad!(
|
||||||
"
|
"
|
||||||
a = []
|
a = []
|
||||||
[[a.b]]
|
[[a.b]]
|
||||||
",
|
",
|
||||||
"duplicate key: `a`"
|
"duplicate key: `a` at line 3 column 9"
|
||||||
);
|
);
|
||||||
bad!(
|
bad!(
|
||||||
"
|
"
|
||||||
|
@ -577,7 +586,7 @@ fn bad_nesting() {
|
||||||
[a.b]
|
[a.b]
|
||||||
c = 2
|
c = 2
|
||||||
",
|
",
|
||||||
"duplicate key: `b` for key `a`"
|
"duplicate key: `b` for key `a` at line 4 column 9"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -608,7 +617,7 @@ fn bad_table_redefine() {
|
||||||
b = {}
|
b = {}
|
||||||
[a.b]
|
[a.b]
|
||||||
",
|
",
|
||||||
"duplicate key: `b` for key `a`"
|
"duplicate key: `b` for key `a` at line 4 column 9"
|
||||||
);
|
);
|
||||||
|
|
||||||
bad!(
|
bad!(
|
||||||
|
@ -637,23 +646,23 @@ fn datetimes() {
|
||||||
t!("2016-09-09T09:09:09.123456789-02:00");
|
t!("2016-09-09T09:09:09.123456789-02:00");
|
||||||
bad!(
|
bad!(
|
||||||
"foo = 2016-09-09T09:09:09.Z",
|
"foo = 2016-09-09T09:09:09.Z",
|
||||||
"failed to parse datetime for key `foo`"
|
"failed to parse datetime for key `foo` at line 1 column 7"
|
||||||
);
|
);
|
||||||
bad!(
|
bad!(
|
||||||
"foo = 2016-9-09T09:09:09Z",
|
"foo = 2016-9-09T09:09:09Z",
|
||||||
"failed to parse datetime for key `foo`"
|
"failed to parse datetime for key `foo` at line 1 column 7"
|
||||||
);
|
);
|
||||||
bad!(
|
bad!(
|
||||||
"foo = 2016-09-09T09:09:09+2:00",
|
"foo = 2016-09-09T09:09:09+2:00",
|
||||||
"failed to parse datetime for key `foo`"
|
"failed to parse datetime for key `foo` at line 1 column 7"
|
||||||
);
|
);
|
||||||
bad!(
|
bad!(
|
||||||
"foo = 2016-09-09T09:09:09-2:00",
|
"foo = 2016-09-09T09:09:09-2:00",
|
||||||
"failed to parse datetime for key `foo`"
|
"failed to parse datetime for key `foo` at line 1 column 7"
|
||||||
);
|
);
|
||||||
bad!(
|
bad!(
|
||||||
"foo = 2016-09-09T09:09:09Z-2:00",
|
"foo = 2016-09-09T09:09:09Z-2:00",
|
||||||
"failed to parse datetime for key `foo`"
|
"failed to parse datetime for key `foo` at line 1 column 7"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -273,7 +273,7 @@ fn type_errors() {
|
||||||
Table(map! {
|
Table(map! {
|
||||||
bar: Value::String("a".to_string())
|
bar: Value::String("a".to_string())
|
||||||
}),
|
}),
|
||||||
"invalid type: string \"a\", expected isize for key `bar`",
|
"invalid type: string \"a\", expected isize for key `bar` at line 1 column 7",
|
||||||
"invalid type: string \"a\", expected isize for key `bar`"
|
"invalid type: string \"a\", expected isize for key `bar`"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,7 +290,7 @@ fn type_errors() {
|
||||||
bar: Value::String("a".to_string())
|
bar: Value::String("a".to_string())
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
"invalid type: string \"a\", expected isize for key `foo.bar`",
|
"invalid type: string \"a\", expected isize for key `foo.bar` at line 2 column 7",
|
||||||
"invalid type: string \"a\", expected isize for key `foo.bar`"
|
"invalid type: string \"a\", expected isize for key `foo.bar`"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue