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 8ab7d3a
Show file tree
Hide file tree
Showing 8 changed files with 517 additions and 127 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
79 changes: 53 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,69 @@ 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 {0:#x}, expected {1:#x} at position {2:#x}\n{3}")]
InvalidBodyLength(u64, u32, u64, String),
}

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 +89,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
143 changes: 72 additions & 71 deletions src/properties/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,145 +150,146 @@ macro_rules! impl_read {
/// ```
macro_rules! impl_read_header {
(options, length $(, $var:ident)*) => {
/// Read GVAS property data from a reader.
#[inline]
fn read_header<R: Read + Seek>(
pub fn read_header<R: Read + Seek>(
reader: &mut R,
options: &mut PropertyOptions,
) -> 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 {
Err($crate::error::DeserializeError::InvalidBodyLength(
end - start,
length,
start,
format!("{result:#?}"),
))?
}

Ok(result)
}
};

(options $(, $var:ident)*) => {
/// Read GVAS property data from a reader.
#[inline]
fn read_header<R: Read + Seek>(
pub fn read_header<R: Read + Seek>(
reader: &mut R,
options: &mut PropertyOptions,
) -> 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{:#?}",
end - start,
length,
result,
);
if end - start != length as u64 {
Err($crate::error::DeserializeError::InvalidBodyLength(
end - start,
length,
start,
format!("{result:#?}"),
))?
}

Ok(result)
}
};

(array_index $(, $var:ident)*) => {
/// Read GVAS property data from a reader.
#[inline]
fn read_header<R: Read + Seek>(
pub fn read_header<R: Read + Seek>(
reader: &mut R,
) -> Result<Self, Error> {
let length = reader.read_u32::<LittleEndian>()?;
let array_index = reader.read_u32::<LittleEndian>()?;
$(
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}",
length,
);
if end - start != length as u64 {
Err($crate::error::DeserializeError::InvalidBodyLength(
end - start,
length,
start,
format!("{result:#?}"),
))?
}

Ok(result)
}
};

($($var:ident $(,)? )*) => {
/// Read GVAS property data from a reader.
#[inline]
fn read_header<R: Read + Seek>(
pub fn read_header<R: Read + Seek>(
reader: &mut R,
) -> 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}",
length,
);
if end - start != length as u64 {
Err($crate::error::DeserializeError::InvalidBodyLength(
end - start,
length,
start,
format!("{result:#?}"),
))?
}

Ok(result)
}
Expand Down
20 changes: 11 additions & 9 deletions src/properties/set_property.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,18 @@ impl SetProperty {
let element_count = cursor.read_u32::<LittleEndian>()?;
let mut properties: Vec<Property> = Vec::with_capacity(element_count as usize);

let total_bytes_per_property = (length - 8) / element_count;
if element_count > 0 {
let total_bytes_per_property = (length - 8) / element_count;

for _ in 0..element_count {
properties.push(Property::new(
cursor,
&property_type,
false,
options,
Some(total_bytes_per_property),
)?)
for _ in 0..element_count {
properties.push(Property::new(
cursor,
&property_type,
false,
options,
Some(total_bytes_per_property),
)?)
}
}

Ok(SetProperty {
Expand Down
Loading

0 comments on commit 8ab7d3a

Please sign in to comment.