diff --git a/src/cursor_ext.rs b/src/cursor_ext.rs index ac05de1..96fc31d 100644 --- a/src/cursor_ext.rs +++ b/src/cursor_ext.rs @@ -71,14 +71,8 @@ impl ReadExt for R { { let value = self.read_i8()?; let result = T::try_from(value).map_err(|_| { - DeserializeError::InvalidEnumValue( - format!( - "No discriminant in enum `{}` matches the value `{}`", - std::any::type_name::(), - value, - ) - .into_boxed_str(), - ) + let name = std::any::type_name::(); + DeserializeError::invalid_enum_value(name, value, self) })?; Ok(result) } diff --git a/src/error.rs b/src/error.rs index cab080f..1e8bd67 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,3 @@ -use num_enum::{TryFromPrimitive, TryFromPrimitiveError}; use std::io; use thiserror::Error; @@ -11,48 +10,71 @@ pub enum DeserializeError { #[error("Invalid header: {0}")] InvalidHeader(String), /// If a value has a size that was unexpected, e.g. UInt32Property has 8 bytes size - #[error("Invalid value size, expected {0} got {1} at position {2}")] + #[error("Invalid value size, expected {0} got {1} at position {2:#x}")] InvalidValueSize(u64, u64, u64), /// If a string has invalid size - #[error("Invalid string size, got {0} at position {1}")] + #[error("Invalid string size {0} at position {1:#x}")] InvalidString(u32, u64), /// If a boolean has invalid value - #[error("Invalid boolean value, got {0} at position {1}")] + #[error("Invalid boolean value, got {0} at position {1:#x}")] InvalidBoolean(u32, u64), /// If a hint is missing. - #[error("Missing hint for struct {0} at path {1}, cursor position: {2}")] + #[error("Missing hint for struct {0} at path {1} at position {2:#x}")] MissingHint(String, String, u64), /// If an argument is missing - #[error("Missing argument: {0} at position {1}")] + #[error("Missing argument: {0} at position {1:#x}")] MissingArgument(String, u64), - /// If an EnumProperty has an invalid enum type - #[error("Invalid enum type {0} at position {1}")] - InvalidEnumType(String, u64), /// If a Property creation fails - #[error("Invalid property {0} at position {1}")] + #[error("Invalid property {0} at position {1:#x}")] InvalidProperty(String, u64), /// Invalid enum value - #[error("{0}")] - InvalidEnumValue(Box), -} - -impl From> for DeserializeError { - fn from(value: TryFromPrimitiveError) -> Self { - DeserializeError::InvalidEnumValue(value.to_string().into_boxed_str()) - } + #[error("No discriminant in enum `{0}` matches the value `{1}` at position {2:#x}")] + InvalidEnumValue(String, i8, u64), + /// Invalid array index header + #[error("Unexpected array_index value {0} at position {1:#x}")] + InvalidArrayIndex(u32, u64), + /// Invalid terminator + #[error("Unexpected terminator value {0} at position {1:#x}")] + InvalidTerminator(u8, u64), + /// Invalid body length + #[error( + "Invalid body length: read_body read {0:#x}, expected {1:#x} at position {3:#x}\n{2:#?}" + )] + InvalidBodyLength(u64, u32, Box, u64), } impl DeserializeError { /// A helper for creating `MissingArgument` errors - pub fn missing_argument(argument_name: &str, stream: &mut S) -> Self { + #[inline] + pub fn missing_argument(argument_name: A, stream: &mut S) -> Self + where + A: Into, + S: io::Seek, + { let position = stream.stream_position().unwrap_or_default(); - Self::MissingArgument(argument_name.to_string(), position) + Self::MissingArgument(argument_name.into(), position) } /// A helper for creating `InvalidProperty` errors - pub fn invalid_property(reason: &str, stream: &mut S) -> Self { + #[inline] + pub fn invalid_property(reason: R, stream: &mut S) -> Self + where + R: Into, + S: io::Seek, + { + let position = stream.stream_position().unwrap_or_default(); + Self::InvalidProperty(reason.into(), position) + } + + /// A helper for creating `InvalidEnumValue` errors + #[inline] + pub fn invalid_enum_value(name: N, value: i8, stream: &mut S) -> Self + where + N: Into, + S: io::Seek, + { let position = stream.stream_position().unwrap_or_default(); - Self::InvalidProperty(reason.to_string(), position) + Self::InvalidEnumValue(name.into(), value, position) } } @@ -69,13 +91,20 @@ pub enum SerializeError { impl SerializeError { /// A helper for creating `InvalidValue` errors - pub fn invalid_value(msg: &str) -> Self { - Self::InvalidValue(msg.to_string()) + pub fn invalid_value(msg: M) -> Self + where + M: Into, + { + Self::InvalidValue(msg.into()) } /// A helper for creating `StructMissingField` errors - pub fn struct_missing_field(type_name: &str, missing_field: &str) -> Self { - Self::StructMissingField(type_name.to_string(), missing_field.to_string()) + pub fn struct_missing_field(type_name: T, missing_field: M) -> Self + where + T: Into, + M: Into, + { + Self::StructMissingField(type_name.into(), missing_field.into()) } } diff --git a/src/properties/array_property.rs b/src/properties/array_property.rs index ac64d29..8a07eb0 100644 --- a/src/properties/array_property.rs +++ b/src/properties/array_property.rs @@ -237,7 +237,7 @@ impl ArrayProperty { guid, structs, }), - Err(p) => Err(SerializeError::invalid_value(&format!( + Err(p) => Err(SerializeError::invalid_value(format!( "Array property_type {} doesn't match property inside array: {:#?}", property_type, p )))?, diff --git a/src/properties/mod.rs b/src/properties/mod.rs index 4539784..1040092 100644 --- a/src/properties/mod.rs +++ b/src/properties/mod.rs @@ -157,32 +157,32 @@ macro_rules! impl_read_header { ) -> Result { let length = reader.read_u32::()?; let array_index = reader.read_u32::()?; - assert_eq!( - array_index, - 0, - "Expected array_index value zero @ {:#x}", - reader.stream_position()? - 4, - ); + if array_index != 0 { + let position = reader.stream_position()? - 4; + Err($crate::error::DeserializeError::InvalidArrayIndex(array_index, position))? + } $( let $var = reader.read_string()?; )* - let separator = reader.read_u8()?; - assert_eq!( - separator, - 0, - "Expected separator value zero @ {:#x}", - reader.stream_position()? - 1, - ); + let terminator = reader.read_u8()?; + if terminator != 0 { + let position = reader.stream_position()? - 1; + Err($crate::error::DeserializeError::InvalidTerminator(terminator, position))? + } let start = reader.stream_position()?; let result = Self::read_body(reader, options, length $(, $var)*)?; let end = reader.stream_position()?; - assert_eq!( - end - start, - length as u64, - "read_body did not read the expected length {:#x}", - length, - ); + if end - start != length as u64 { + let reason = format!( + "read_body read {:#x}, expected {:#x} at position {:#x}\n{:#?}", + end - start, + length, + start, + result, + ); + Err($crate::error::DeserializeError::InvalidProperty(reason, start))? + } Ok(result) } @@ -196,22 +196,18 @@ macro_rules! impl_read_header { ) -> Result { let length = reader.read_u32::()?; let array_index = reader.read_u32::()?; - assert_eq!( - array_index, - 0, - "Expected array_index value zero @ {:#x}", - reader.stream_position()? - 4, - ); + if array_index != 0 { + let position = reader.stream_position()? - 4; + Err($crate::error::DeserializeError::InvalidArrayIndex(array_index, position))? + } $( let $var = reader.read_string()?; )* - let separator = reader.read_u8()?; - assert_eq!( - separator, - 0, - "Expected separator value zero @ {:#x}", - reader.stream_position()? - 1, - ); + let terminator = reader.read_u8()?; + if terminator != 0 { + let position = reader.stream_position()? - 1; + Err($crate::error::DeserializeError::InvalidTerminator(terminator, position))? + } let start = reader.stream_position()?; let result = Self::read_body(reader, options $(, $var)*)?; @@ -219,9 +215,10 @@ macro_rules! impl_read_header { assert_eq!( end - start, length as u64, - "read_body read {:#x}, expected {:#x}\n{:#?}", + "read_body read {:#x}, expected {:#x} at position {:#x}\n{:#?}", end - start, length, + start, result, ); @@ -239,13 +236,11 @@ macro_rules! impl_read_header { $( let $var = reader.read_string()?; )* - let separator = reader.read_u8()?; - assert_eq!( - separator, - 0, - "Expected separator value zero @ {:#x}", - reader.stream_position()? - 1, - ); + let terminator = reader.read_u8()?; + if terminator != 0 { + let position = reader.stream_position()? - 1; + Err($crate::error::DeserializeError::InvalidTerminator(terminator, position))? + } let start = reader.stream_position()?; let result = Self::read_body(reader, array_index $(, Some($var))*)?; @@ -253,8 +248,11 @@ macro_rules! impl_read_header { assert_eq!( end - start, length as u64, - "read_body did not read the expected length {:#x}", + "read_body read {:#x}, expected {:#x} at position {:#x}\n{:#?}", + end - start, length, + start, + result, ); Ok(result) @@ -268,17 +266,18 @@ macro_rules! impl_read_header { ) -> Result { let length = reader.read_u32::()?; let array_index = reader.read_u32::()?; - assert_eq!(array_index, 0, "Expected array_index value zero @ {:#x}", reader.stream_position()? - 4); + if array_index != 0 { + let position = reader.stream_position()? - 4; + Err($crate::error::DeserializeError::InvalidArrayIndex(array_index, position))? + } $( let $var = reader.read_string()?; )* - let separator = reader.read_u8()?; - assert_eq!( - separator, - 0, - "Expected separator value zero @ {:#x}", - reader.stream_position()? - 1, - ); + let terminator = reader.read_u8()?; + if terminator != 0 { + let position = reader.stream_position()? - 1; + Err($crate::error::DeserializeError::InvalidTerminator(terminator, position))? + } let start = reader.stream_position()?; let result = Self::read_body(reader $(, Some($var))*)?; @@ -286,8 +285,11 @@ macro_rules! impl_read_header { assert_eq!( end - start, length as u64, - "read_body did not read the expected length {:#x}", + "read_body read {:#x}, expected {:#x} at position {:#x}\n{:#?}", + end - start, length, + start, + result, ); Ok(result) diff --git a/tests/gvas_tests/test_cursor.rs b/tests/gvas_tests/test_cursor.rs index d7244af..a3bec23 100644 --- a/tests/gvas_tests/test_cursor.rs +++ b/tests/gvas_tests/test_cursor.rs @@ -40,24 +40,18 @@ fn test_read_string() -> Result<(), Error> { // Null let mut cursor = Cursor::new(vec![0u8; 4]); - let err = cursor.read_string().expect_err("Expected err"); - assert_eq!(err.to_string(), "Invalid string size, got 0 at position 4"); + let string = cursor.read_string().expect_err("Expected err").to_string(); + assert_eq!(string, "Invalid string size 0 at position 0x4"); // Missing null terminator let mut cursor = Cursor::new(vec![1u8, 0u8, 0u8, 0u8, b't']); - let err = cursor.read_string().expect_err("Expected err"); - assert_eq!( - err.to_string(), - "Invalid string terminator 116 at position 5" - ); + let string = cursor.read_string().expect_err("Expected err").to_string(); + assert_eq!(string, "Invalid string terminator 116 at position 5"); // Missing null terminator, UTF-16 let mut cursor = Cursor::new(vec![0xffu8, 0xffu8, 0xffu8, 0xffu8, b't', b'e']); - let err = cursor.read_string().expect_err("Expected err"); - assert_eq!( - err.to_string(), - "Invalid string terminator 25972 at position 6" - ); + let string = cursor.read_string().expect_err("Expected err").to_string(); + assert_eq!(string, "Invalid string terminator 25972 at position 6"); Ok(()) }