diff --git a/CHANGELOG.md b/CHANGELOG.md index f95a0b963..240b614fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix issue [#359](https://github.com/ron-rs/ron/issues/359) with `DeserializeSeed` support ([#360](https://github.com/ron-rs/ron/pull/360)) - Bump MSRV to 1.46.0 ([#361](https://github.com/ron-rs/ron/pull/361)) - Fix issue [#337](https://github.com/ron-rs/ron/issues/337) by removing `decimal_floats` PrettyConfig option and unconditional decimals in floats ([#363](https://github.com/ron-rs/ron/pull/363)) +- Fix issue [#203](https://github.com/ron-rs/ron/issues/203) with full de error positioning ([#356](https://github.com/ron-rs/ron/pull/356)) ## [0.7.0] - 2021-10-22 diff --git a/src/de/mod.rs b/src/de/mod.rs index 6108f088f..e0d09f46a 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -1,11 +1,12 @@ /// Deserialization module. -pub use crate::error::{Error, ErrorCode, Position, Result}; +pub use crate::error::{Error, Position, SpannedError}; use serde::de::{self, DeserializeSeed, Deserializer as SerdeError, Visitor}; use std::{borrow::Cow, io, str}; use self::{id::IdDeserializer, tag::TagDeserializer}; use crate::{ + error::{Result, SpannedResult}, extensions::Extensions, options::Options, parse::{AnyNum, Bytes, ParsedStr}, @@ -29,19 +30,19 @@ pub struct Deserializer<'de> { impl<'de> Deserializer<'de> { // Cannot implement trait here since output is tied to input lifetime 'de. #[allow(clippy::should_implement_trait)] - pub fn from_str(input: &'de str) -> Result { + pub fn from_str(input: &'de str) -> SpannedResult { Self::from_str_with_options(input, Options::default()) } - pub fn from_bytes(input: &'de [u8]) -> Result { + pub fn from_bytes(input: &'de [u8]) -> SpannedResult { Self::from_bytes_with_options(input, Options::default()) } - pub fn from_str_with_options(input: &'de str, options: Options) -> Result { + pub fn from_str_with_options(input: &'de str, options: Options) -> SpannedResult { Self::from_bytes_with_options(input.as_bytes(), options) } - pub fn from_bytes_with_options(input: &'de [u8], options: Options) -> Result { + pub fn from_bytes_with_options(input: &'de [u8], options: Options) -> SpannedResult { let mut deserializer = Deserializer { bytes: Bytes::new(input)?, newtype_variant: false, @@ -55,11 +56,15 @@ impl<'de> Deserializer<'de> { pub fn remainder(&self) -> Cow<'_, str> { String::from_utf8_lossy(self.bytes.bytes()) } + + pub fn span_error(&self, code: Error) -> SpannedError { + self.bytes.span_error(code) + } } /// A convenience function for building a deserializer /// and deserializing a value of type `T` from a reader. -pub fn from_reader(rdr: R) -> Result +pub fn from_reader(rdr: R) -> SpannedResult where R: io::Read, T: de::DeserializeOwned, @@ -69,7 +74,7 @@ where /// A convenience function for building a deserializer /// and deserializing a value of type `T` from a string. -pub fn from_str<'a, T>(s: &'a str) -> Result +pub fn from_str<'a, T>(s: &'a str) -> SpannedResult where T: de::Deserialize<'a>, { @@ -78,7 +83,7 @@ where /// A convenience function for building a deserializer /// and deserializing a value of type `T` from bytes. -pub fn from_bytes<'a, T>(s: &'a [u8]) -> Result +pub fn from_bytes<'a, T>(s: &'a [u8]) -> SpannedResult where T: de::Deserialize<'a>, { @@ -94,7 +99,7 @@ impl<'de> Deserializer<'de> { if self.bytes.bytes().is_empty() { Ok(()) } else { - self.bytes.err(ErrorCode::TrailingCharacters) + Err(Error::TrailingCharacters) } } @@ -191,7 +196,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { b'.' => self.deserialize_f64(visitor), b'"' | b'r' => self.deserialize_string(visitor), b'\'' => self.deserialize_char(visitor), - other => self.bytes.err(ErrorCode::UnexpectedByte(other as char)), + other => Err(Error::UnexpectedByte(other as char)), } } @@ -334,7 +339,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { match res { Ok(byte_buf) => visitor.visit_byte_buf(byte_buf), - Err(err) => self.bytes.err(ErrorCode::Base64Error(err)), + Err(err) => Err(Error::Base64Error(err)), } } @@ -359,10 +364,10 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { if self.bytes.consume(")") { Ok(v) } else { - self.bytes.err(ErrorCode::ExpectedOptionEnd) + Err(Error::ExpectedOptionEnd) } } else { - self.bytes.err(ErrorCode::ExpectedOption) + Err(Error::ExpectedOption) } } @@ -376,7 +381,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { visitor.visit_unit() } else { - self.bytes.err(ErrorCode::ExpectedUnit) + Err(Error::ExpectedUnit) } } @@ -415,12 +420,12 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { if self.bytes.consume(")") { Ok(value) } else { - self.bytes.err(ErrorCode::ExpectedStructEnd) + Err(Error::ExpectedStructLikeEnd) } } else if name.is_empty() { - self.bytes.err(ErrorCode::ExpectedStruct) + Err(Error::ExpectedStructLike) } else { - self.bytes.err(ErrorCode::ExpectedNamedStruct(name)) + Err(Error::ExpectedNamedStructLike(name)) } } @@ -437,10 +442,10 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { if self.bytes.consume("]") { Ok(value) } else { - self.bytes.err(ErrorCode::ExpectedArrayEnd) + Err(Error::ExpectedArrayEnd) } } else { - self.bytes.err(ErrorCode::ExpectedArray) + Err(Error::ExpectedArray) } } @@ -458,10 +463,10 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { if old_newtype_variant || self.bytes.consume(")") { Ok(value) } else { - self.bytes.err(ErrorCode::ExpectedArrayEnd) + Err(Error::ExpectedStructLikeEnd) } } else { - self.bytes.err(ErrorCode::ExpectedArray) + Err(Error::ExpectedStructLike) } } @@ -478,7 +483,10 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { self.bytes.consume_struct_name(name)?; } - self.deserialize_tuple(len, visitor) + self.deserialize_tuple(len, visitor).map_err(|e| match e { + Error::ExpectedStructLike if !name.is_empty() => Error::ExpectedNamedStructLike(name), + e => e, + }) } fn deserialize_map(mut self, visitor: V) -> Result @@ -494,10 +502,10 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { if self.bytes.consume("}") { Ok(value) } else { - self.bytes.err(ErrorCode::ExpectedMapEnd) + Err(Error::ExpectedMapEnd) } } else { - self.bytes.err(ErrorCode::ExpectedMap) + Err(Error::ExpectedMap) } } @@ -526,12 +534,12 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { if old_newtype_variant || self.bytes.consume(")") { Ok(value) } else { - self.bytes.err(ErrorCode::ExpectedStructEnd) + Err(Error::ExpectedStructLikeEnd) } } else if name.is_empty() { - self.bytes.err(ErrorCode::ExpectedStruct) + Err(Error::ExpectedStructLike) } else { - self.bytes.err(ErrorCode::ExpectedNamedStruct(name)) + Err(Error::ExpectedNamedStructLike(name)) } } @@ -553,9 +561,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { where V: Visitor<'de>, { - visitor.visit_str( - str::from_utf8(self.bytes.identifier()?).map_err(|e| self.bytes.error(e.into()))?, - ) + visitor.visit_str(str::from_utf8(self.bytes.identifier()?).map_err(Error::from)?) } fn deserialize_ignored_any(self, visitor: V) -> Result @@ -581,10 +587,6 @@ impl<'a, 'de> CommaSeparated<'a, 'de> { } } - fn err(&self, kind: ErrorCode) -> Result { - self.de.bytes.err(kind) - } - fn has_element(&mut self) -> Result { self.de.bytes.skip_ws()?; @@ -597,7 +599,7 @@ impl<'a, 'de> CommaSeparated<'a, 'de> { // No trailing comma but terminator (false, false) => Ok(false), // No trailing comma or terminator - (false, true) => self.err(ErrorCode::ExpectedComma), + (false, true) => Err(Error::ExpectedComma), } } } @@ -655,7 +657,7 @@ impl<'de, 'a> de::MapAccess<'de> for CommaSeparated<'a, 'de> { Ok(res) } else { - self.err(ErrorCode::ExpectedMapColon) + Err(Error::ExpectedMapColon) } } } @@ -717,10 +719,10 @@ impl<'de, 'a> de::VariantAccess<'de> for Enum<'a, 'de> { if self.de.bytes.consume(")") { Ok(val) } else { - self.de.bytes.err(ErrorCode::ExpectedStructEnd) + Err(Error::ExpectedStructLikeEnd) } } else { - self.de.bytes.err(ErrorCode::ExpectedStruct) + Err(Error::ExpectedStructLike) } } diff --git a/src/de/tests.rs b/src/de/tests.rs index 364120865..3e37e4e9d 100644 --- a/src/de/tests.rs +++ b/src/de/tests.rs @@ -1,7 +1,11 @@ use serde::Deserialize; use serde_bytes; -use super::*; +use crate::{ + de::from_str, + error::{Error, Position, SpannedError, SpannedResult}, + parse::{AnyNum, Bytes}, +}; #[derive(Debug, PartialEq, Deserialize)] struct EmptyStruct1; @@ -149,8 +153,8 @@ y: 2.0 // 2! ); } -fn err(kind: ErrorCode, line: usize, col: usize) -> Result { - Err(Error { +fn err(kind: Error, line: usize, col: usize) -> SpannedResult { + Err(SpannedError { code: kind, position: Position { line, col }, }) @@ -158,31 +162,31 @@ fn err(kind: ErrorCode, line: usize, col: usize) -> Result { #[test] fn test_err_wrong_value() { - use self::ErrorCode::*; + use self::Error::*; use std::collections::HashMap; assert_eq!(from_str::("'c'"), err(ExpectedFloat, 1, 1)); assert_eq!(from_str::("'c'"), err(ExpectedString, 1, 1)); assert_eq!(from_str::>("'c'"), err(ExpectedMap, 1, 1)); - assert_eq!(from_str::<[u8; 5]>("'c'"), err(ExpectedArray, 1, 1)); + assert_eq!(from_str::<[u8; 5]>("'c'"), err(ExpectedStructLike, 1, 1)); assert_eq!(from_str::>("'c'"), err(ExpectedArray, 1, 1)); assert_eq!(from_str::("'c'"), err(ExpectedIdentifier, 1, 1)); assert_eq!( from_str::("'c'"), - err(ExpectedNamedStruct("MyStruct"), 1, 1) + err(ExpectedNamedStructLike("MyStruct"), 1, 1) ); assert_eq!( from_str::("NotMyStruct(x: 4, y: 2)"), err( - ExpectedStructName { + ExpectedDifferentStructName { expected: "MyStruct", found: String::from("NotMyStruct") }, 1, - 1 + 12 ) ); - assert_eq!(from_str::<(u8, bool)>("'c'"), err(ExpectedArray, 1, 1)); + assert_eq!(from_str::<(u8, bool)>("'c'"), err(ExpectedStructLike, 1, 1)); assert_eq!(from_str::("notabool"), err(ExpectedBoolean, 1, 1)); assert_eq!( @@ -234,38 +238,38 @@ fn rename() { #[test] fn forgot_apostrophes() { - let de: Result<(i32, String)> = from_str("(4, \"Hello)"); + let de: SpannedResult<(i32, String)> = from_str("(4, \"Hello)"); - assert!(match de { - Err(Error { - code: ErrorCode::ExpectedStringEnd, + assert!(matches!( + de, + Err(SpannedError { + code: Error::ExpectedStringEnd, position: _, - }) => true, - _ => false, - }); + }) + )); } #[test] fn expected_attribute() { - let de: Result = from_str("#\"Hello\""); + let de: SpannedResult = from_str("#\"Hello\""); - assert_eq!(de, err(ErrorCode::ExpectedAttribute, 1, 2)); + assert_eq!(de, err(Error::ExpectedAttribute, 1, 2)); } #[test] fn expected_attribute_end() { - let de: Result = from_str("#![enable(unwrap_newtypes) \"Hello\""); + let de: SpannedResult = from_str("#![enable(unwrap_newtypes) \"Hello\""); - assert_eq!(de, err(ErrorCode::ExpectedAttributeEnd, 1, 28)); + assert_eq!(de, err(Error::ExpectedAttributeEnd, 1, 28)); } #[test] fn invalid_attribute() { - let de: Result = from_str("#![enable(invalid)] \"Hello\""); + let de: SpannedResult = from_str("#![enable(invalid)] \"Hello\""); assert_eq!( de, - err(ErrorCode::NoSuchExtension("invalid".to_string()), 1, 18) + err(Error::NoSuchExtension("invalid".to_string()), 1, 18) ); } @@ -273,7 +277,7 @@ fn invalid_attribute() { fn multiple_attributes() { #[derive(Debug, Deserialize, PartialEq)] struct New(String); - let de: Result = + let de: SpannedResult = from_str("#![enable(unwrap_newtypes)] #![enable(unwrap_newtypes)] \"Hello\""); assert_eq!(de, Ok(New("Hello".to_owned()))); @@ -281,7 +285,7 @@ fn multiple_attributes() { #[test] fn uglified_attribute() { - let de: Result<()> = from_str( + let de: SpannedResult<()> = from_str( "# !\ // We definitely want to add a comment here [\t\tenable( // best style ever diff --git a/src/de/value.rs b/src/de/value.rs index 1c4555b74..041c16f97 100644 --- a/src/de/value.rs +++ b/src/de/value.rs @@ -5,20 +5,18 @@ use serde::{ Deserialize, Deserializer, }; -use crate::{ - de, - value::{Map, Number, Value}, -}; +use crate::error::SpannedResult; +use crate::value::{Map, Number, Value}; impl std::str::FromStr for Value { - type Err = de::Error; + type Err = crate::SpannedError; /// Creates a value from a string reference. - fn from_str(s: &str) -> de::Result { + fn from_str(s: &str) -> SpannedResult { let mut de = super::Deserializer::from_str(s)?; - let val = Value::deserialize(&mut de)?; - de.end()?; + let val = Value::deserialize(&mut de).map_err(|e| de.span_error(e))?; + de.end().map_err(|e| de.span_error(e))?; Ok(val) } @@ -231,12 +229,12 @@ mod tests { #[test] fn test_tuples_error() { - use crate::de::{Error, ErrorCode, Position}; + use crate::de::{Error, Position, SpannedError}; assert_eq!( Value::from_str("Foo:").unwrap_err(), - Error { - code: ErrorCode::TrailingCharacters, + SpannedError { + code: Error::TrailingCharacters, position: Position { col: 4, line: 1 } }, ); diff --git a/src/error.rs b/src/error.rs index c7f59077f..e7d279d61 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,16 +4,17 @@ use std::{error::Error as StdError, fmt, io, str::Utf8Error, string::FromUtf8Err /// This type represents all possible errors that can occur when /// serializing or deserializing RON data. #[derive(Clone, Debug, PartialEq)] -pub struct Error { - pub code: ErrorCode, +pub struct SpannedError { + pub code: Error, pub position: Position, } -pub type Result = std::result::Result; +pub type Result = std::result::Result; +pub type SpannedResult = std::result::Result; #[derive(Clone, Debug, PartialEq)] #[non_exhaustive] -pub enum ErrorCode { +pub enum Error { Io(String), Message(String), Base64Error(base64::DecodeError), @@ -32,13 +33,13 @@ pub enum ErrorCode { ExpectedMap, ExpectedMapColon, ExpectedMapEnd, - ExpectedStructName { + ExpectedDifferentStructName { expected: &'static str, found: String, }, - ExpectedStruct, - ExpectedNamedStruct(&'static str), - ExpectedStructEnd, + ExpectedStructLike, + ExpectedNamedStructLike(&'static str), + ExpectedStructLikeEnd, ExpectedUnit, ExpectedString, ExpectedStringEnd, @@ -58,7 +59,7 @@ pub enum ErrorCode { TrailingCharacters, } -impl fmt::Display for Error { +impl fmt::Display for SpannedError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if (self.position == Position { line: 0, col: 0 }) { write!(f, "{}", self.code) @@ -68,52 +69,52 @@ impl fmt::Display for Error { } } -impl fmt::Display for ErrorCode { +impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { - ErrorCode::Io(ref s) => f.write_str(s), - ErrorCode::Message(ref s) => f.write_str(s), - ErrorCode::Base64Error(ref e) => fmt::Display::fmt(e, f), - ErrorCode::Eof => f.write_str("Unexpected end of RON"), - ErrorCode::ExpectedArray => f.write_str("Expected opening `[`"), - ErrorCode::ExpectedArrayEnd => f.write_str("Expected closing `]`"), - ErrorCode::ExpectedAttribute => f.write_str("Expected an `#![enable(...)]` attribute"), - ErrorCode::ExpectedAttributeEnd => { + Error::Io(ref s) => f.write_str(s), + Error::Message(ref s) => f.write_str(s), + Error::Base64Error(ref e) => fmt::Display::fmt(e, f), + Error::Eof => f.write_str("Unexpected end of RON"), + Error::ExpectedArray => f.write_str("Expected opening `[`"), + Error::ExpectedArrayEnd => f.write_str("Expected closing `]`"), + Error::ExpectedAttribute => f.write_str("Expected an `#![enable(...)]` attribute"), + Error::ExpectedAttributeEnd => { f.write_str("Expected closing `)]` after the enable attribute") } - ErrorCode::ExpectedBoolean => f.write_str("Expected boolean"), - ErrorCode::ExpectedComma => f.write_str("Expected comma"), - ErrorCode::ExpectedChar => f.write_str("Expected char"), - ErrorCode::ExpectedFloat => f.write_str("Expected float"), - ErrorCode::ExpectedInteger => f.write_str("Expected integer"), - ErrorCode::ExpectedOption => f.write_str("Expected option"), - ErrorCode::ExpectedOptionEnd => f.write_str("Expected closing `)`"), - ErrorCode::ExpectedMap => f.write_str("Expected opening `{`"), - ErrorCode::ExpectedMapColon => f.write_str("Expected colon"), - ErrorCode::ExpectedMapEnd => f.write_str("Expected closing `}`"), - ErrorCode::ExpectedStructName { + Error::ExpectedBoolean => f.write_str("Expected boolean"), + Error::ExpectedComma => f.write_str("Expected comma"), + Error::ExpectedChar => f.write_str("Expected char"), + Error::ExpectedFloat => f.write_str("Expected float"), + Error::ExpectedInteger => f.write_str("Expected integer"), + Error::ExpectedOption => f.write_str("Expected option"), + Error::ExpectedOptionEnd => f.write_str("Expected closing `)`"), + Error::ExpectedMap => f.write_str("Expected opening `{`"), + Error::ExpectedMapColon => f.write_str("Expected colon"), + Error::ExpectedMapEnd => f.write_str("Expected closing `}`"), + Error::ExpectedDifferentStructName { expected, ref found, } => write!(f, "Expected struct '{}' but found '{}'", expected, found), - ErrorCode::ExpectedStruct => f.write_str("Expected opening `(`"), - ErrorCode::ExpectedNamedStruct(name) => { + Error::ExpectedStructLike => f.write_str("Expected opening `(`"), + Error::ExpectedNamedStructLike(name) => { write!(f, "Expected opening `(` for struct '{}'", name) } - ErrorCode::ExpectedStructEnd => f.write_str("Expected closing `)`"), - ErrorCode::ExpectedUnit => f.write_str("Expected unit"), - ErrorCode::ExpectedString => f.write_str("Expected string"), - ErrorCode::ExpectedStringEnd => f.write_str("Expected end of string"), - ErrorCode::ExpectedIdentifier => f.write_str("Expected identifier"), - ErrorCode::InvalidEscape(e) => write!(f, "Invalid escape sequence '{}'", e), - ErrorCode::IntegerOutOfBounds => f.write_str("Integer is out of bounds"), - ErrorCode::NoSuchExtension(ref name) => write!(f, "No RON extension '{}'", name), - ErrorCode::Utf8Error(ref e) => fmt::Display::fmt(e, f), - ErrorCode::UnclosedBlockComment => f.write_str("Unclosed block comment"), - ErrorCode::UnderscoreAtBeginning => { + Error::ExpectedStructLikeEnd => f.write_str("Expected closing `)`"), + Error::ExpectedUnit => f.write_str("Expected unit"), + Error::ExpectedString => f.write_str("Expected string"), + Error::ExpectedStringEnd => f.write_str("Expected end of string"), + Error::ExpectedIdentifier => f.write_str("Expected identifier"), + Error::InvalidEscape(s) => f.write_str(s), + Error::IntegerOutOfBounds => f.write_str("Integer is out of bounds"), + Error::NoSuchExtension(ref name) => write!(f, "No RON extension named '{}'", name), + Error::Utf8Error(ref e) => fmt::Display::fmt(e, f), + Error::UnclosedBlockComment => f.write_str("Unclosed block comment"), + Error::UnderscoreAtBeginning => { f.write_str("Unexpected leading underscore in an integer") } - ErrorCode::UnexpectedByte(ref byte) => write!(f, "Unexpected byte {:?}", byte), - ErrorCode::TrailingCharacters => f.write_str("Non-whitespace trailing characters"), + Error::UnexpectedByte(ref byte) => write!(f, "Unexpected byte {:?}", byte), + Error::TrailingCharacters => f.write_str("Non-whitespace trailing characters"), } } } @@ -130,52 +131,50 @@ impl fmt::Display for Position { } } -impl de::Error for Error { +impl ser::Error for Error { fn custom(msg: T) -> Self { - Error { - code: ErrorCode::Message(msg.to_string()), - position: Position { line: 0, col: 0 }, - } + Error::Message(msg.to_string()) } } -impl ser::Error for Error { +impl de::Error for Error { fn custom(msg: T) -> Self { - Error { - code: ErrorCode::Message(msg.to_string()), - position: Position { line: 0, col: 0 }, - } + Error::Message(msg.to_string()) } } +impl StdError for SpannedError {} impl StdError for Error {} -impl From for ErrorCode { +impl From for Error { fn from(e: Utf8Error) -> Self { - ErrorCode::Utf8Error(e) + Error::Utf8Error(e) } } -impl From for ErrorCode { +impl From for Error { fn from(e: FromUtf8Error) -> Self { - ErrorCode::Utf8Error(e.utf8_error()) + Error::Utf8Error(e.utf8_error()) } } -impl From for Error { - fn from(e: Utf8Error) -> Self { - Error { - code: ErrorCode::Utf8Error(e), - position: Position { line: 0, col: 0 }, - } +impl From for Error { + fn from(e: io::Error) -> Self { + Error::Io(e.to_string()) } } -impl From for Error { +impl From for SpannedError { fn from(e: io::Error) -> Self { - Error { - code: ErrorCode::Io(e.to_string()), + SpannedError { + code: e.into(), position: Position { line: 0, col: 0 }, } } } + +impl From for Error { + fn from(e: SpannedError) -> Self { + e.code + } +} diff --git a/src/lib.rs b/src/lib.rs index 1e768a1bf..0be423405 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,7 +70,7 @@ pub mod extensions; pub mod options; pub use de::{from_str, Deserializer}; -pub use error::{Error, Result}; +pub use error::{Result, SpannedError}; pub use options::Options; pub use ser::{to_string, Serializer}; pub use value::{Map, Number, Value}; diff --git a/src/options.rs b/src/options.rs index b6d28e243..1e3511812 100644 --- a/src/options.rs +++ b/src/options.rs @@ -5,7 +5,7 @@ use std::io; use serde::{de, ser, Deserialize, Serialize}; use crate::de::Deserializer; -use crate::error::Result; +use crate::error::{Result, SpannedResult}; use crate::extensions::Extensions; use crate::ser::{PrettyConfig, Serializer}; @@ -65,7 +65,7 @@ impl Options { impl Options { /// A convenience function for building a deserializer /// and deserializing a value of type `T` from a reader. - pub fn from_reader(&self, mut rdr: R) -> Result + pub fn from_reader(&self, mut rdr: R) -> SpannedResult where R: io::Read, T: de::DeserializeOwned, @@ -78,7 +78,7 @@ impl Options { /// A convenience function for building a deserializer /// and deserializing a value of type `T` from a string. - pub fn from_str<'a, T>(&self, s: &'a str) -> Result + pub fn from_str<'a, T>(&self, s: &'a str) -> SpannedResult where T: de::Deserialize<'a>, { @@ -87,7 +87,7 @@ impl Options { /// A convenience function for building a deserializer /// and deserializing a value of type `T` from bytes. - pub fn from_bytes<'a, T>(&self, s: &'a [u8]) -> Result + pub fn from_bytes<'a, T>(&self, s: &'a [u8]) -> SpannedResult where T: de::Deserialize<'a>, { @@ -97,7 +97,7 @@ impl Options { /// A convenience function for building a deserializer /// and deserializing a value of type `T` from a reader /// and a seed. - pub fn from_reader_seed(&self, mut rdr: R, seed: S) -> Result + pub fn from_reader_seed(&self, mut rdr: R, seed: S) -> SpannedResult where R: io::Read, S: for<'a> de::DeserializeSeed<'a, Value = T>, @@ -111,7 +111,7 @@ impl Options { /// A convenience function for building a deserializer /// and deserializing a value of type `T` from a string /// and a seed. - pub fn from_str_seed<'a, S, T>(&self, s: &'a str, seed: S) -> Result + pub fn from_str_seed<'a, S, T>(&self, s: &'a str, seed: S) -> SpannedResult where S: de::DeserializeSeed<'a, Value = T>, { @@ -121,15 +121,17 @@ impl Options { /// A convenience function for building a deserializer /// and deserializing a value of type `T` from bytes /// and a seed. - pub fn from_bytes_seed<'a, S, T>(&self, s: &'a [u8], seed: S) -> Result + pub fn from_bytes_seed<'a, S, T>(&self, s: &'a [u8], seed: S) -> SpannedResult where S: de::DeserializeSeed<'a, Value = T>, { let mut deserializer = Deserializer::from_bytes_with_options(s, self.clone())?; - let value = seed.deserialize(&mut deserializer)?; + let value = seed + .deserialize(&mut deserializer) + .map_err(|e| deserializer.span_error(e))?; - deserializer.end()?; + deserializer.end().map_err(|e| deserializer.span_error(e))?; Ok(value) } diff --git a/src/parse.rs b/src/parse.rs index fbf3b8db5..3f9953a0b 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -6,7 +6,7 @@ use std::{ }; use crate::{ - error::{Error, ErrorCode, Position, Result}, + error::{Error, Position, Result, SpannedError, SpannedResult}, extensions::Extensions, }; @@ -105,11 +105,10 @@ pub enum AnyNum { #[derive(Clone, Copy, Debug)] pub struct Bytes<'a> { - /// Bits set according to `Extension` enum. + /// Bits set according to the `Extensions` enum. pub exts: Extensions, bytes: &'a [u8], - column: usize, - line: usize, + cursor: Position, } #[cfg(feature = "integer128")] @@ -122,30 +121,37 @@ pub(crate) type LargeSInt = i128; pub(crate) type LargeSInt = i64; impl<'a> Bytes<'a> { - pub fn new(bytes: &'a [u8]) -> Result { + pub fn new(bytes: &'a [u8]) -> SpannedResult { let mut b = Bytes { - bytes, - column: 1, exts: Extensions::empty(), - line: 1, + bytes, + cursor: Position { line: 1, col: 1 }, }; - b.skip_ws()?; + b.skip_ws().map_err(|e| b.span_error(e))?; + // Loop over all extensions attributes loop { - let attribute = b.extensions()?; + let attribute = b.extensions().map_err(|e| b.span_error(e))?; if attribute.is_empty() { break; } b.exts |= attribute; - b.skip_ws()?; + b.skip_ws().map_err(|e| b.span_error(e))?; } Ok(b) } + pub fn span_error(&self, code: Error) -> SpannedError { + SpannedError { + code, + position: self.cursor, + } + } + pub fn advance(&mut self, bytes: usize) -> Result<()> { for _ in 0..bytes { self.advance_single()?; @@ -156,10 +162,10 @@ impl<'a> Bytes<'a> { pub fn advance_single(&mut self) -> Result<()> { if self.peek_or_eof()? == b'\n' { - self.line += 1; - self.column = 1; + self.cursor.line += 1; + self.cursor.col = 1; } else { - self.column += 1; + self.cursor.col += 1; } self.bytes = &self.bytes[1..]; @@ -188,13 +194,13 @@ impl<'a> Bytes<'a> { let num_bytes = self.next_bytes_contained_in(is_int_char); if num_bytes == 0 { - return self.err(ErrorCode::ExpectedInteger); + return Err(Error::ExpectedInteger); } let s = unsafe { from_utf8_unchecked(&self.bytes[0..num_bytes]) }; if s.as_bytes()[0] == b'_' { - return self.err(ErrorCode::UnderscoreAtBeginning); + return Err(Error::UnderscoreAtBeginning); } fn calc_num( @@ -211,17 +217,17 @@ impl<'a> Bytes<'a> { } if num_acc.checked_mul_ext(base) { - return bytes.err(ErrorCode::IntegerOutOfBounds); + return Err(Error::IntegerOutOfBounds); } let digit = bytes.decode_hex(byte)?; if digit >= base { - return bytes.err(ErrorCode::ExpectedInteger); + return Err(Error::ExpectedInteger); } if f(&mut num_acc, digit) { - return bytes.err(ErrorCode::IntegerOutOfBounds); + return Err(Error::IntegerOutOfBounds); } } @@ -229,9 +235,9 @@ impl<'a> Bytes<'a> { } let res = if sign > 0 { - calc_num(&*self, s, base, T::checked_add_ext) + calc_num(self, s, base, T::checked_add_ext) } else { - calc_num(&*self, s, base, T::checked_sub_ext) + calc_num(self, s, base, T::checked_sub_ext) }; let _ = self.advance(num_bytes); @@ -345,7 +351,7 @@ impl<'a> Bytes<'a> { } else if self.consume("false") { Ok(false) } else { - self.err(ErrorCode::ExpectedBoolean) + Err(Error::ExpectedBoolean) } } @@ -355,7 +361,7 @@ impl<'a> Bytes<'a> { pub fn char(&mut self) -> Result { if !self.consume("'") { - return self.err(ErrorCode::ExpectedChar); + return Err(Error::ExpectedChar); } let c = self.peek_or_eof()?; @@ -372,15 +378,13 @@ impl<'a> Bytes<'a> { let pos: usize = self.bytes[..max] .iter() .position(|&x| x == b'\'') - .ok_or_else(|| self.error(ErrorCode::ExpectedChar))?; - let s = from_utf8(&self.bytes[0..pos]).map_err(|e| self.error(e.into()))?; + .ok_or(Error::ExpectedChar)?; + let s = from_utf8(&self.bytes[0..pos]).map_err(Error::from)?; let mut chars = s.chars(); - let first = chars - .next() - .ok_or_else(|| self.error(ErrorCode::ExpectedChar))?; + let first = chars.next().ok_or(Error::ExpectedChar)?; if chars.next().is_some() { - return self.err(ErrorCode::ExpectedChar); + return Err(Error::ExpectedChar); } let _ = self.advance(pos); @@ -389,7 +393,7 @@ impl<'a> Bytes<'a> { }; if !self.consume("'") { - return self.err(ErrorCode::ExpectedChar); + return Err(Error::ExpectedChar); } Ok(c) @@ -447,29 +451,25 @@ impl<'a> Bytes<'a> { pub fn consume_struct_name(&mut self, ident: &'static str) -> Result { if self.check_ident("") { Ok(false) + } else if ident.is_empty() { + Err(Error::ExpectedStructLike) } else if self.check_ident(ident) { let _ = self.advance(ident.len()); Ok(true) - } else if ident.is_empty() { - Err(self.error(ErrorCode::ExpectedStruct)) } else { - // Create a working copy - let mut bytes = *self; - // If the following is not even an identifier, then a missing // opening `(` seems more likely - let maybe_ident = bytes.identifier().map_err(|e| Error { - code: ErrorCode::ExpectedNamedStruct(ident), - position: e.position, - })?; + let maybe_ident = self + .identifier() + .map_err(|_| Error::ExpectedNamedStructLike(ident))?; - let found = std::str::from_utf8(maybe_ident).map_err(|e| bytes.error(e.into()))?; + let found = std::str::from_utf8(maybe_ident).map_err(Error::from)?; - Err(self.error(ErrorCode::ExpectedStructName { + Err(Error::ExpectedDifferentStructName { expected: ident, found: String::from(found), - })) + }) } } @@ -504,23 +504,9 @@ impl<'a> Bytes<'a> { Ok(peek) } - pub fn err(&self, kind: ErrorCode) -> Result { - Err(self.error(kind)) - } - - pub fn error(&self, kind: ErrorCode) -> Error { - Error { - code: kind, - position: Position { - line: self.line, - col: self.column, - }, - } - } - - pub fn expect_byte(&mut self, byte: u8, error: ErrorCode) -> Result<()> { + pub fn expect_byte(&mut self, byte: u8, error: Error) -> Result<()> { self.eat_byte() - .and_then(|b| if b == byte { Ok(()) } else { self.err(error) }) + .and_then(|b| if b == byte { Ok(()) } else { Err(error) }) } /// Returns the extensions bit mask. @@ -530,7 +516,7 @@ impl<'a> Bytes<'a> { } if !self.consume_all(&["#", "!", "[", "enable", "("])? { - return self.err(ErrorCode::ExpectedAttribute); + return Err(Error::ExpectedAttribute); } self.skip_ws()?; @@ -539,9 +525,7 @@ impl<'a> Bytes<'a> { loop { let ident = self.identifier()?; let extension = Extensions::from_ident(ident).ok_or_else(|| { - self.error(ErrorCode::NoSuchExtension( - from_utf8(ident).unwrap().to_owned(), - )) + Error::NoSuchExtension(String::from_utf8_lossy(ident).into_owned()) })?; extensions |= extension; @@ -550,7 +534,7 @@ impl<'a> Bytes<'a> { // If we have no comma but another item, return an error if !comma && self.check_ident_other_char(0) { - return self.err(ErrorCode::ExpectedComma); + return Err(Error::ExpectedComma); } // If there's no comma, assume the list ended. @@ -566,7 +550,7 @@ impl<'a> Bytes<'a> { if self.consume_all(&[")", "]"])? { Ok(extensions) } else { - Err(self.error(ErrorCode::ExpectedAttributeEnd)) + Err(Error::ExpectedAttributeEnd) } } @@ -583,7 +567,7 @@ impl<'a> Bytes<'a> { let num_bytes = self.next_bytes_contained_in(is_float_char); let s = unsafe { from_utf8_unchecked(&self.bytes[0..num_bytes]) }; - let res = FromStr::from_str(s).map_err(|_| self.error(ErrorCode::ExpectedFloat)); + let res = FromStr::from_str(s).map_err(|_| Error::ExpectedFloat); let _ = self.advance(num_bytes); @@ -593,24 +577,20 @@ impl<'a> Bytes<'a> { pub fn identifier(&mut self) -> Result<&'a [u8]> { let next = self.peek_or_eof()?; if !is_ident_first_char(next) { - return self.err(ErrorCode::ExpectedIdentifier); + return Err(Error::ExpectedIdentifier); } // If the next two bytes signify the start of a raw string literal, // return an error. let length = if next == b'r' { - match self - .bytes - .get(1) - .ok_or_else(|| self.error(ErrorCode::Eof))? - { - b'"' => return self.err(ErrorCode::ExpectedIdentifier), + match self.bytes.get(1).ok_or(Error::Eof)? { + b'"' => return Err(Error::ExpectedIdentifier), b'#' => { let after_next = self.bytes.get(2).cloned().unwrap_or_default(); - //Note: it's important to check this before advancing forward, so that + // Note: it's important to check this before advancing forward, so that // the value-type deserializer can fall back to parsing it differently. if !is_ident_raw_char(after_next) { - return self.err(ErrorCode::ExpectedIdentifier); + return Err(Error::ExpectedIdentifier); } // skip "r#" let _ = self.advance(2); @@ -657,15 +637,15 @@ impl<'a> Bytes<'a> { } pub fn skip_ws(&mut self) -> Result<()> { - while self.peek().map_or(false, is_whitespace_char) { - let _ = self.advance_single(); - } + loop { + while self.peek().map_or(false, is_whitespace_char) { + let _ = self.advance_single(); + } - if self.skip_comment()? { - self.skip_ws()?; + if !self.skip_comment()? { + return Ok(()); + } } - - Ok(()) } pub fn peek(&self) -> Option { @@ -673,10 +653,7 @@ impl<'a> Bytes<'a> { } pub fn peek_or_eof(&self) -> Result { - self.bytes - .get(0) - .cloned() - .ok_or_else(|| self.error(ErrorCode::Eof)) + self.bytes.get(0).cloned().ok_or(Error::Eof) } pub fn signed_integer(&mut self) -> Result @@ -704,7 +681,7 @@ impl<'a> Bytes<'a> { } else if self.consume("r") { self.raw_string() } else { - self.err(ErrorCode::ExpectedString) + Err(Error::ExpectedString) } } @@ -716,10 +693,10 @@ impl<'a> Bytes<'a> { .iter() .enumerate() .find(|&(_, &b)| b == b'\\' || b == b'"') - .ok_or_else(|| self.error(ErrorCode::ExpectedStringEnd))?; + .ok_or(Error::ExpectedStringEnd)?; if *end_or_escape == b'"' { - let s = from_utf8(&self.bytes[..i]).map_err(|e| self.error(e.into()))?; + let s = from_utf8(&self.bytes[..i]).map_err(Error::from)?; // Advance by the number of bytes of the string // + 1 for the `"`. @@ -747,8 +724,7 @@ impl<'a> Bytes<'a> { .iter() .enumerate() .find(|&(_, &b)| b == b'\\' || b == b'"') - .ok_or(ErrorCode::Eof) - .map_err(|e| self.error(e))?; + .ok_or(Error::ExpectedStringEnd)?; i = new_i; s.extend_from_slice(&self.bytes[..i]); @@ -756,7 +732,7 @@ impl<'a> Bytes<'a> { if *end_or_escape == b'"' { let _ = self.advance(i + 1); - let s = String::from_utf8(s).map_err(|e| self.error(e.into()))?; + let s = String::from_utf8(s).map_err(Error::from)?; break Ok(ParsedStr::Allocated(s)); } } @@ -769,7 +745,7 @@ impl<'a> Bytes<'a> { let _ = self.advance(num_hashes); if !self.consume("\"") { - return self.err(ErrorCode::ExpectedString); + return Err(Error::ExpectedString); } let ending = [&[b'"'], hashes].concat(); @@ -777,9 +753,9 @@ impl<'a> Bytes<'a> { .bytes .windows(num_hashes + 1) .position(|window| window == ending.as_slice()) - .ok_or_else(|| self.error(ErrorCode::ExpectedStringEnd))?; + .ok_or(Error::ExpectedStringEnd)?; - let s = from_utf8(&self.bytes[..i]).map_err(|e| self.error(e.into()))?; + let s = from_utf8(&self.bytes[..i]).map_err(Error::from)?; // Advance by the number of bytes of the string // + `num_hashes` + 1 for the `"`. @@ -816,7 +792,7 @@ impl<'a> Bytes<'a> { c @ b'0'..=b'9' => Ok(c - b'0'), c @ b'a'..=b'f' => Ok(10 + c - b'a'), c @ b'A'..=b'F' => Ok(10 + c - b'A'), - _ => self.err(ErrorCode::InvalidEscape("Non-hex digit found")), + _ => Err(Error::InvalidEscape("Non-hex digit found")), } } @@ -830,7 +806,7 @@ impl<'a> Bytes<'a> { b't' => '\t', b'x' => self.decode_ascii_escape()? as char, b'u' => { - self.expect_byte(b'{', ErrorCode::InvalidEscape("Missing {"))?; + self.expect_byte(b'{', Error::InvalidEscape("Missing { in Unicode escape"))?; let mut bytes: u32 = 0; let mut num_digits = 0; @@ -852,17 +828,19 @@ impl<'a> Bytes<'a> { } if num_digits == 0 { - return self.err(ErrorCode::InvalidEscape( - "Expected 1-6 digits, got 0 digits", + return Err(Error::InvalidEscape( + "Expected 1-6 digits, got 0 digits in Unicode escape", )); } - self.expect_byte(b'}', ErrorCode::InvalidEscape("No } at the end"))?; - char_from_u32(bytes) - .ok_or_else(|| self.error(ErrorCode::InvalidEscape("Not a valid char")))? + self.expect_byte( + b'}', + Error::InvalidEscape("No } at the end of Unicode escape"), + )?; + char_from_u32(bytes).ok_or(Error::InvalidEscape("Not a valid char"))? } _ => { - return self.err(ErrorCode::InvalidEscape("Unknown escape character")); + return Err(Error::InvalidEscape("Unknown escape character")); } }; @@ -888,7 +866,7 @@ impl<'a> Bytes<'a> { .count(); if self.bytes.is_empty() { - return self.err(ErrorCode::UnclosedBlockComment); + return Err(Error::UnclosedBlockComment); } let _ = self.advance(bytes); @@ -899,12 +877,11 @@ impl<'a> Bytes<'a> { } else if self.consume("*/") { level -= 1; } else { - self.eat_byte() - .map_err(|_| self.error(ErrorCode::UnclosedBlockComment))?; + self.eat_byte().map_err(|_| Error::UnclosedBlockComment)?; } } } - b => return self.err(ErrorCode::UnexpectedByte(b as char)), + b => return Err(Error::UnexpectedByte(b as char)), } Ok(true) diff --git a/src/value.rs b/src/value.rs index 0038f2aa1..565bfa05e 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,12 +1,5 @@ //! Value module. -use serde::{ - de::{ - DeserializeOwned, DeserializeSeed, Deserializer, Error as SerdeError, MapAccess, SeqAccess, - Visitor, - }, - forward_to_deserialize_any, Deserialize, Serialize, -}; use std::{ cmp::{Eq, Ordering}, hash::{Hash, Hasher}, @@ -14,7 +7,13 @@ use std::{ ops::{Index, IndexMut}, }; -use crate::de::{Error as RonError, Result}; +use serde::{ + de::{DeserializeOwned, DeserializeSeed, Deserializer, MapAccess, SeqAccess, Visitor}, + forward_to_deserialize_any, Deserialize, Serialize, +}; + +use crate::de::Error; +use crate::error::Result; /// A `Value` to `Value` map. /// @@ -340,7 +339,7 @@ impl Value { /// Deserializer implementation for RON `Value`. /// This does not support enums (because `Value` doesn't store them). impl<'de> Deserializer<'de> for Value { - type Error = RonError; + type Error = Error; forward_to_deserialize_any! { bool f32 f64 char str string bytes @@ -399,7 +398,7 @@ impl<'de> Deserializer<'de> for Value { { match self { Value::Number(Number::Integer(i)) => visitor.visit_i64(i), - v => Err(RonError::custom(format!("Expected a number, got {:?}", v))), + v => Err(Error::Message(format!("Expected a number, got {:?}", v))), } } @@ -430,7 +429,7 @@ impl<'de> Deserializer<'de> for Value { { match self { Value::Number(Number::Integer(i)) => visitor.visit_u64(i as u64), - v => Err(RonError::custom(format!("Expected a number, got {:?}", v))), + v => Err(Error::Message(format!("Expected a number, got {:?}", v))), } } } @@ -441,7 +440,7 @@ struct MapAccessor { } impl<'de> MapAccess<'de> for MapAccessor { - type Error = RonError; + type Error = Error; fn next_key_seed(&mut self, seed: K) -> Result> where @@ -470,7 +469,7 @@ struct Seq { } impl<'de> SeqAccess<'de> for Seq { - type Error = RonError; + type Error = Error; fn next_element_seed(&mut self, seed: T) -> Result> where diff --git a/tests/152_bitflags.rs b/tests/152_bitflags.rs index 7e784f398..b51fd5510 100644 --- a/tests/152_bitflags.rs +++ b/tests/152_bitflags.rs @@ -33,6 +33,7 @@ bitflags_serial! { fn test_bitflags() { // Test case provided by jaynus in // https://github.com/ron-rs/ron/issues/152#issue-421298302 + let flag_good = TestGood::ONE | TestGood::TWO; let json_ser_good = serde_json::ser::to_string(&flag_good).unwrap(); diff --git a/tests/203_error_positions.rs b/tests/203_error_positions.rs new file mode 100644 index 000000000..f1537aa97 --- /dev/null +++ b/tests/203_error_positions.rs @@ -0,0 +1,63 @@ +use std::num::NonZeroU32; + +use ron::error::{Error, Position, SpannedError}; +use serde::de::{value::Error as DeError, Deserialize, IntoDeserializer}; + +#[derive(Debug, serde::Deserialize, PartialEq)] +enum Test { + TupleVariant(i32, NonZeroU32), + StructVariant { a: bool, b: NonZeroU32, c: i32 }, +} + +#[test] +fn test_error_positions() { + assert_eq!( + ron::from_str::("NotAVariant"), + Err(SpannedError { + code: Error::Message(String::from( + "unknown variant `NotAVariant`, expected `TupleVariant` or `StructVariant`" + )), + position: Position { line: 1, col: 12 }, + }) + ); + + assert_eq!( + ron::from_str::("TupleVariant(1, 0)"), + Err(SpannedError { + code: Error::Message( + NonZeroU32::deserialize(IntoDeserializer::::into_deserializer(0_u32)) + .unwrap_err() + .to_string() + ), + position: Position { line: 1, col: 18 }, + }) + ); + + assert_eq!( + ron::from_str::("StructVariant(a: true, b: 0, c: -42)"), + Err(SpannedError { + code: Error::Message( + NonZeroU32::deserialize(IntoDeserializer::::into_deserializer(0_u32)) + .unwrap_err() + .to_string() + ), + position: Position { line: 1, col: 28 }, + }) + ); + + assert_eq!( + ron::from_str::("StructVariant(a: true, c: -42)"), + Err(SpannedError { + code: Error::Message(String::from("missing field `b`")), + position: Position { line: 1, col: 30 }, + }) + ); + + assert_eq!( + ron::from_str::("StructVariant(a: true, b: 1, a: false, c: -42)"), + Err(SpannedError { + code: Error::Message(String::from("duplicate field `a`")), + position: Position { line: 1, col: 31 }, + }) + ); +} diff --git a/tests/250_variant_newtypes.rs b/tests/250_variant_newtypes.rs index c0c7b2c6c..f4c76606c 100644 --- a/tests/250_variant_newtypes.rs +++ b/tests/250_variant_newtypes.rs @@ -1,8 +1,7 @@ use std::collections::HashMap; use ron::{ - de::from_str, error::ErrorCode, extensions::Extensions, ser::to_string_pretty, - ser::PrettyConfig, + de::from_str, error::Error, extensions::Extensions, ser::to_string_pretty, ser::PrettyConfig, }; use serde::{Deserialize, Serialize}; @@ -77,13 +76,13 @@ fn test_deserialise_tuple_newtypes() { from_str::(r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeUnit(Unit)"#) .unwrap_err() .code, - ErrorCode::ExpectedStructEnd, + Error::ExpectedStructLikeEnd, ); assert_eq!( from_str::(r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeUnit(())"#) .unwrap_err() .code, - ErrorCode::ExpectedStructEnd, + Error::ExpectedStructLikeEnd, ); assert_eq!( from_str::(r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeUnit()"#).unwrap(), @@ -96,13 +95,13 @@ fn test_deserialise_tuple_newtypes() { ) .unwrap_err() .code, - ErrorCode::ExpectedInteger, + Error::ExpectedInteger, ); assert_eq!( from_str::(r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeNewtype((4))"#) .unwrap_err() .code, - ErrorCode::ExpectedInteger, + Error::ExpectedInteger, ); assert_eq!( from_str::(r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeNewtype(4)"#) @@ -124,7 +123,7 @@ fn test_deserialise_tuple_newtypes() { ) .unwrap_err() .code, - ErrorCode::ExpectedInteger, + Error::ExpectedInteger, ); assert_eq!( from_str::(r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeTuple(4, false)"#) @@ -138,7 +137,7 @@ fn test_deserialise_tuple_newtypes() { ) .unwrap_err() .code, - ErrorCode::ExpectedInteger, + Error::ExpectedInteger, ); assert_eq!( from_str::( @@ -146,7 +145,7 @@ fn test_deserialise_tuple_newtypes() { ) .unwrap_err() .code, - ErrorCode::ExpectedInteger, + Error::ExpectedInteger, ); assert_eq!( from_str::( @@ -162,7 +161,7 @@ fn test_deserialise_tuple_newtypes() { ) .unwrap_err() .code, - ErrorCode::ExpectedMapColon, + Error::ExpectedMapColon, ); assert_eq!( from_str::( @@ -170,7 +169,7 @@ fn test_deserialise_tuple_newtypes() { ) .unwrap_err() .code, - ErrorCode::ExpectedIdentifier, + Error::ExpectedIdentifier, ); assert_eq!( from_str::( @@ -195,7 +194,7 @@ fn test_deserialise_tuple_newtypes() { from_str::(r#"#![enable(unwrap_variant_newtypes)] TupleNewtypeEnum(C 4, false)"#) .unwrap_err() .code, - ErrorCode::ExpectedArray, + Error::ExpectedStructLike, ); assert_eq!( from_str::( @@ -210,7 +209,7 @@ fn test_deserialise_tuple_newtypes() { ) .unwrap_err() .code, - ErrorCode::ExpectedStruct, + Error::ExpectedStructLike, ); assert_eq!( from_str::( @@ -238,7 +237,7 @@ fn test_deserialise_tuple_newtypes() { ) .unwrap_err() .code, - ErrorCode::ExpectedOption, + Error::ExpectedOption, ); assert_eq!( from_str::(r#"#![enable(unwrap_variant_newtypes, implicit_some)] TupleNewtypeOption(a: 4, b: false)"#).unwrap(), diff --git a/tests/256_comma_error.rs b/tests/256_comma_error.rs index d32df3abb..4a873e476 100644 --- a/tests/256_comma_error.rs +++ b/tests/256_comma_error.rs @@ -1,6 +1,6 @@ #![allow(dead_code)] -use ron::error::{Error, ErrorCode, Position}; +use ron::error::{Error, Position, SpannedError}; #[derive(Debug, serde::Deserialize)] struct Test { @@ -17,8 +17,8 @@ fn test_missing_comma_error() { assert_eq!( ron::from_str::<(i32, i32)>(tuple_string).unwrap_err(), - Error { - code: ErrorCode::ExpectedComma, + SpannedError { + code: Error::ExpectedComma, position: Position { line: 3, col: 9 } } ); @@ -31,8 +31,8 @@ fn test_missing_comma_error() { assert_eq!( ron::from_str::>(list_string).unwrap_err(), - Error { - code: ErrorCode::ExpectedComma, + SpannedError { + code: Error::ExpectedComma, position: Position { line: 4, col: 9 } } ); @@ -44,8 +44,8 @@ fn test_missing_comma_error() { assert_eq!( ron::from_str::(struct_string).unwrap_err(), - Error { - code: ErrorCode::ExpectedComma, + SpannedError { + code: Error::ExpectedComma, position: Position { line: 3, col: 9 } } ); @@ -57,8 +57,8 @@ fn test_missing_comma_error() { assert_eq!( ron::from_str::>(map_string).unwrap_err(), - Error { - code: ErrorCode::ExpectedComma, + SpannedError { + code: Error::ExpectedComma, position: Position { line: 3, col: 9 } } ); diff --git a/tests/301_struct_name_mismatch.rs b/tests/301_struct_name_mismatch.rs index 0286c80f4..063cdac36 100644 --- a/tests/301_struct_name_mismatch.rs +++ b/tests/301_struct_name_mismatch.rs @@ -1,4 +1,4 @@ -use ron::error::{Error, ErrorCode, Position}; +use ron::error::{Error, Position, SpannedError}; use serde::Deserialize; #[derive(Debug, Deserialize, PartialEq)] @@ -25,18 +25,18 @@ fn test_unit_struct_name_mismatch() { ); assert_eq!( ron::from_str::("MyUnit Struct"), - Err(Error { - code: ErrorCode::ExpectedStructName { + Err(SpannedError { + code: Error::ExpectedDifferentStructName { expected: "MyUnitStruct", found: String::from("MyUnit") }, - position: Position { line: 1, col: 1 } + position: Position { line: 1, col: 7 } }), ); assert_eq!( ron::from_str::("42"), - Err(Error { - code: ErrorCode::ExpectedNamedStruct("MyUnitStruct"), + Err(SpannedError { + code: Error::ExpectedNamedStructLike("MyUnitStruct"), position: Position { line: 1, col: 1 } }), ); @@ -54,18 +54,18 @@ fn test_tuple_struct_name_mismatch() { ); assert_eq!( ron::from_str::("MyTypleStruct(true, 42)"), - Err(Error { - code: ErrorCode::ExpectedStructName { + Err(SpannedError { + code: Error::ExpectedDifferentStructName { expected: "MyTupleStruct", found: String::from("MyTypleStruct") }, - position: Position { line: 1, col: 1 } + position: Position { line: 1, col: 14 } }), ); assert_eq!( ron::from_str::("42"), - Err(Error { - code: ErrorCode::ExpectedNamedStruct("MyTupleStruct"), + Err(SpannedError { + code: Error::ExpectedNamedStructLike("MyTupleStruct"), position: Position { line: 1, col: 1 } }), ); @@ -83,18 +83,18 @@ fn test_newtype_struct_name_mismatch() { ); assert_eq!( ron::from_str::("MyNewtypeStrucl((true, 42))"), - Err(Error { - code: ErrorCode::ExpectedStructName { + Err(SpannedError { + code: Error::ExpectedDifferentStructName { expected: "MyNewtypeStruct", found: String::from("MyNewtypeStrucl") }, - position: Position { line: 1, col: 1 } + position: Position { line: 1, col: 16 } }), ); assert_eq!( ron::from_str::("42"), - Err(Error { - code: ErrorCode::ExpectedNamedStruct("MyNewtypeStruct"), + Err(SpannedError { + code: Error::ExpectedNamedStructLike("MyNewtypeStruct"), position: Position { line: 1, col: 1 } }), ); @@ -112,18 +112,18 @@ fn test_struct_name_mismatch() { ); assert_eq!( ron::from_str::("MuStryct(a: true, b: 42)"), - Err(Error { - code: ErrorCode::ExpectedStructName { + Err(SpannedError { + code: Error::ExpectedDifferentStructName { expected: "MyStruct", found: String::from("MuStryct") }, - position: Position { line: 1, col: 1 } + position: Position { line: 1, col: 9 } }), ); assert_eq!( ron::from_str::("42"), - Err(Error { - code: ErrorCode::ExpectedNamedStruct("MyStruct"), + Err(SpannedError { + code: Error::ExpectedNamedStructLike("MyStruct"), position: Position { line: 1, col: 1 } }), ); diff --git a/tests/comments.rs b/tests/comments.rs index f5d6f73c1..2b77de618 100644 --- a/tests/comments.rs +++ b/tests/comments.rs @@ -1,4 +1,4 @@ -use ron::de::{from_str, Error as RonErr, ErrorCode, Position}; +use ron::de::{from_str, Error, Position, SpannedError as RonErr}; #[test] fn test_simple() { @@ -45,7 +45,7 @@ fn test_unclosed() { " ), Err(RonErr { - code: ErrorCode::UnclosedBlockComment, + code: Error::UnclosedBlockComment, position: Position { col: 1, line: 9 } }) ); diff --git a/tests/escape.rs b/tests/escape.rs index 20cc87ee1..591b8ad76 100644 --- a/tests/escape.rs +++ b/tests/escape.rs @@ -8,6 +8,9 @@ fn test_escape_basic() { assert_eq!(from_str::("\"\\x07\"").unwrap(), "\x07"); assert_eq!(from_str::("\"\\u{7}\"").unwrap(), "\x07"); + + assert_eq!(from_str::("\'\\x07\'").unwrap(), '\x07'); + assert_eq!(from_str::("\'\\u{7}\'").unwrap(), '\x07'); } fn check_same(t: T) diff --git a/tests/numbers.rs b/tests/numbers.rs index 26b97a53d..4717ddb9f 100644 --- a/tests/numbers.rs +++ b/tests/numbers.rs @@ -1,10 +1,33 @@ use ron::de::from_str; +use ron::error::{Error, Position, SpannedError}; #[test] fn test_hex() { assert_eq!(from_str("0x507"), Ok(0x507)); assert_eq!(from_str("0x1A5"), Ok(0x1A5)); assert_eq!(from_str("0x53C537"), Ok(0x53C537)); + + assert_eq!( + from_str::("0x"), + Err(SpannedError { + code: Error::ExpectedInteger, + position: Position { line: 1, col: 3 }, + }) + ); + assert_eq!( + from_str::("0x_1"), + Err(SpannedError { + code: Error::UnderscoreAtBeginning, + position: Position { line: 1, col: 3 }, + }) + ); + assert_eq!( + from_str::("0xFFF"), + Err(SpannedError { + code: Error::IntegerOutOfBounds, + position: Position { line: 1, col: 6 }, + }) + ); } #[test] @@ -12,6 +35,28 @@ fn test_bin() { assert_eq!(from_str("0b101"), Ok(0b101)); assert_eq!(from_str("0b001"), Ok(0b001)); assert_eq!(from_str("0b100100"), Ok(0b100100)); + + assert_eq!( + from_str::("0b"), + Err(SpannedError { + code: Error::ExpectedInteger, + position: Position { line: 1, col: 3 }, + }) + ); + assert_eq!( + from_str::("0b_1"), + Err(SpannedError { + code: Error::UnderscoreAtBeginning, + position: Position { line: 1, col: 3 }, + }) + ); + assert_eq!( + from_str::("0b111111111"), + Err(SpannedError { + code: Error::IntegerOutOfBounds, + position: Position { line: 1, col: 12 }, + }) + ); } #[test] @@ -19,4 +64,48 @@ fn test_oct() { assert_eq!(from_str("0o1461"), Ok(0o1461)); assert_eq!(from_str("0o051"), Ok(0o051)); assert_eq!(from_str("0o150700"), Ok(0o150700)); + + assert_eq!( + from_str::("0o"), + Err(SpannedError { + code: Error::ExpectedInteger, + position: Position { line: 1, col: 3 }, + }) + ); + assert_eq!( + from_str::("0o_1"), + Err(SpannedError { + code: Error::UnderscoreAtBeginning, + position: Position { line: 1, col: 3 }, + }) + ); + assert_eq!( + from_str::("0o77777"), + Err(SpannedError { + code: Error::IntegerOutOfBounds, + position: Position { line: 1, col: 8 }, + }) + ); +} + +#[test] +fn test_dec() { + assert_eq!(from_str("1461"), Ok(1461)); + assert_eq!(from_str("51"), Ok(51)); + assert_eq!(from_str("150700"), Ok(150700)); + + assert_eq!( + from_str::("-_1"), + Err(SpannedError { + code: Error::UnderscoreAtBeginning, + position: Position { line: 1, col: 2 }, + }) + ); + assert_eq!( + from_str::("256"), + Err(SpannedError { + code: Error::IntegerOutOfBounds, + position: Position { line: 1, col: 4 }, + }) + ); } diff --git a/tests/value.rs b/tests/value.rs index 55eb29a94..8256d3cf9 100644 --- a/tests/value.rs +++ b/tests/value.rs @@ -71,15 +71,15 @@ fn seq() { #[test] fn unit() { - use ron::error::{Error, ErrorCode, Position}; + use ron::error::{Error, Position, SpannedError}; assert_eq!("()".parse(), Ok(Value::Unit)); assert_eq!("Foo".parse(), Ok(Value::Unit)); assert_eq!( "".parse::(), - Err(Error { - code: ErrorCode::Eof, + Err(SpannedError { + code: Error::Eof, position: Position { col: 1, line: 1 } }) );