Skip to content

Commit

Permalink
Replace asserts with errors
Browse files Browse the repository at this point in the history
  • Loading branch information
scottanderson committed Jun 4, 2024
1 parent ad672f7 commit f613790
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 97 deletions.
10 changes: 2 additions & 8 deletions src/cursor_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,8 @@ impl<R: Read + Seek> 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::<T>(),
value,
)
.into_boxed_str(),
)
let name = std::any::type_name::<T>();
DeserializeError::invalid_enum_value(name, value, self)
})?;
Ok(result)
}
Expand Down
81 changes: 55 additions & 26 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use num_enum::{TryFromPrimitive, TryFromPrimitiveError};
use std::io;

use thiserror::Error;
Expand All @@ -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<str>),
}

impl<T: TryFromPrimitive> From<TryFromPrimitiveError<T>> for DeserializeError {
fn from(value: TryFromPrimitiveError<T>) -> 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<dyn std::fmt::Debug>, u64),
}

impl DeserializeError {
/// A helper for creating `MissingArgument` errors
pub fn missing_argument<S: io::Seek>(argument_name: &str, stream: &mut S) -> Self {
#[inline]
pub fn missing_argument<A, S>(argument_name: A, stream: &mut S) -> Self
where
A: Into<String>,
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<S: io::Seek>(reason: &str, stream: &mut S) -> Self {
#[inline]
pub fn invalid_property<R, S>(reason: R, stream: &mut S) -> Self
where
R: Into<String>,
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<N, S>(name: N, value: i8, stream: &mut S) -> Self
where
N: Into<String>,
S: io::Seek,
{
let position = stream.stream_position().unwrap_or_default();
Self::InvalidProperty(reason.to_string(), position)
Self::InvalidEnumValue(name.into(), value, position)
}
}

Expand All @@ -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<M>(msg: M) -> Self
where
M: Into<String>,
{
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<T, M>(type_name: T, missing_field: M) -> Self
where
T: Into<String>,
M: Into<String>,
{
Self::StructMissingField(type_name.into(), missing_field.into())
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/properties/array_property.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
)))?,
Expand Down
102 changes: 52 additions & 50 deletions src/properties/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,32 +157,32 @@ macro_rules! impl_read_header {
) -> Result<Self, Error> {
let length = reader.read_u32::<LittleEndian>()?;
let array_index = reader.read_u32::<LittleEndian>()?;
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)
}
Expand All @@ -196,32 +196,29 @@ macro_rules! impl_read_header {
) -> Result<Self, Error> {
let length = reader.read_u32::<LittleEndian>()?;
let array_index = reader.read_u32::<LittleEndian>()?;
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)*)?;
let end = reader.stream_position()?;
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,
);

Expand All @@ -239,22 +236,23 @@ 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))*)?;
let end = reader.stream_position()?;
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)
Expand All @@ -268,26 +266,30 @@ macro_rules! impl_read_header {
) -> Result<Self, Error> {
let length = reader.read_u32::<LittleEndian>()?;
let array_index = reader.read_u32::<LittleEndian>()?;
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))*)?;
let end = reader.stream_position()?;
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)
Expand Down
18 changes: 6 additions & 12 deletions tests/gvas_tests/test_cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}

0 comments on commit f613790

Please sign in to comment.