From 3b9c1ae079be2d93a13d5991d83dea724fa4cb96 Mon Sep 17 00:00:00 2001 From: Matt Bilker Date: Sat, 9 Nov 2019 08:20:20 +0000 Subject: [PATCH] text_reader: add custom error type --- src/error.rs | 61 +++++++-------------- src/text_reader.rs | 133 ++++++++++++++++++++++++++++++++++++--------- 2 files changed, 127 insertions(+), 67 deletions(-) diff --git a/src/error.rs b/src/error.rs index c2d6e43..293d7a3 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,10 +1,7 @@ use std::error::Error; -//use std::fmt; use std::io; use std::num::{ParseFloatError, ParseIntError}; use std::result::Result as StdResult; -use std::str::Utf8Error; -use std::string::FromUtf8Error; use quick_xml::Error as QuickXmlError; use rustc_hex::FromHexError; @@ -12,9 +9,10 @@ use snafu::Snafu; use crate::byte_buffer::ByteBufferError; use crate::encoding_type::EncodingError; -use crate::node_types::{StandardType, UnknownKbinType}; +use crate::node_types::StandardType; use crate::reader::ReaderError; use crate::sixbit::SixbitError; +use crate::text_reader::TextReaderError; use crate::value::Value; use crate::writer::WriterError; @@ -23,28 +21,12 @@ pub type Result = StdResult; #[derive(Debug, Snafu)] #[snafu(visibility = "pub(crate)")] pub enum KbinError { - #[snafu(display("Unable to write {} header field", field))] - HeaderWrite { - field: &'static str, - source: io::Error, - }, - #[snafu(display("Unable to read bytes or not enough data read"))] DataConvert { source: io::Error }, #[snafu(display("No node collection found"))] NoNodeCollection, - #[snafu(display("Failed to interpret string as UTF-8"))] - Utf8 { source: FromUtf8Error }, - - #[snafu(display("Failed to interpret slice as UTF-8"))] - Utf8Slice { source: Utf8Error }, - - // TODO(felix): remove when text reader has own error type - #[snafu(display("Invalid kbin type read"))] - InvalidKbinType { source: UnknownKbinType }, - #[snafu(display( "Size Mismatch, type: {}, expected size: {}, actual size: {}", node_type, @@ -114,6 +96,12 @@ pub enum KbinError { source: EncodingError, }, + #[snafu(display("Failed to handle sixbit string operation"))] + Sixbit { + #[snafu(backtrace)] + source: SixbitError, + }, + #[snafu(display("Failed to read binary XML"))] Reader { #[snafu(backtrace)] @@ -126,30 +114,16 @@ pub enum KbinError { source: WriterError, }, - #[snafu(display("Failed to handle sixbit string operation"))] - Sixbit { + #[snafu(display("Failed to read text XML"))] + TextReader { #[snafu(backtrace)] - source: SixbitError, + source: TextReaderError, }, #[snafu(display("Error handling XML"))] XmlError { source: QuickXmlError }, } -impl From for KbinError { - #[inline] - fn from(source: FromUtf8Error) -> Self { - KbinError::Utf8 { source } - } -} - -impl From for KbinError { - #[inline] - fn from(source: Utf8Error) -> Self { - KbinError::Utf8Slice { source } - } -} - impl From for KbinError { #[inline] fn from(source: ByteBufferError) -> Self { @@ -164,6 +138,13 @@ impl From for KbinError { } } +impl From for KbinError { + #[inline] + fn from(source: SixbitError) -> Self { + KbinError::Sixbit { source } + } +} + impl From for KbinError { #[inline] fn from(source: ReaderError) -> Self { @@ -178,10 +159,10 @@ impl From for KbinError { } } -impl From for KbinError { +impl From for KbinError { #[inline] - fn from(source: SixbitError) -> Self { - KbinError::Sixbit { source } + fn from(source: TextReaderError) -> Self { + KbinError::TextReader { source } } } diff --git a/src/text_reader.rs b/src/text_reader.rs index cd47001..ffdd273 100644 --- a/src/text_reader.rs +++ b/src/text_reader.rs @@ -1,19 +1,89 @@ -use std::str; +use std::num::{ParseFloatError, ParseIntError}; +use std::str::{self, Utf8Error}; +use std::string::FromUtf8Error; use bytes::{BufMut, Bytes, BytesMut}; use quick_xml::events::attributes::Attributes; use quick_xml::events::{BytesStart, BytesText, Event}; +use quick_xml::Error as QuickXmlError; use quick_xml::Reader; -use snafu::ResultExt; +use snafu::{ResultExt, Snafu}; -use crate::encoding_type::EncodingType; -use crate::error::*; +use crate::encoding_type::{EncodingError, EncodingType}; use crate::node::{Key, NodeCollection, NodeData, NodeDefinition}; -use crate::node_types::StandardType; +use crate::node_types::{StandardType, UnknownKbinType}; use crate::value::Value; const EMPTY_STRING_DATA: &[u8] = &[0]; +#[derive(Debug, Snafu)] +pub enum TextReaderError { + #[snafu(display("Invalid kbin type found"))] + InvalidKbinType { source: UnknownKbinType }, + + #[snafu(display("Invalid encoding type read from declaration"))] + InvalidEncoding { source: EncodingError }, + + #[snafu(display( + "Mismatched binary node length and size attribute value (value length: {}, size attribute: {})", + len, + size + ))] + MismatchedBinaryNodeLength { len: usize, size: usize }, + + #[snafu(display("No node data found"))] + NoNodeData, + + #[snafu(display("Failed to interpret bytes as UTF-8"))] + Utf8 { source: FromUtf8Error }, + + #[snafu(display("Failed to interpret byte slice as UTF-8"))] + Utf8Slice { source: Utf8Error }, + + #[snafu(display("Failed to parse integer input as node type {}", node_type))] + StringParseInt { + node_type: &'static str, + source: ParseIntError, + }, + + #[snafu(display("Failed to parse float input as node type {}", node_type))] + StringParseFloat { + node_type: &'static str, + source: ParseFloatError, + }, + + #[snafu(display("Failed to decode value from string for node type {}", node_type))] + ValueDecode { + node_type: StandardType, + #[snafu(source(from(crate::KbinError, Box::new)))] + source: Box, + }, + + #[snafu(display("Failed to encode value for node type {}", node_type))] + ValueEncode { + node_type: StandardType, + #[snafu(source(from(crate::KbinError, Box::new)))] + source: Box, + }, + + #[snafu(display("Failed to handle XML operation"))] + Xml { source: QuickXmlError }, +} + +impl From for TextReaderError { + #[inline] + fn from(source: Utf8Error) -> Self { + Self::Utf8Slice { source } + } +} + +impl From for TextReaderError { + #[inline] + fn from(source: QuickXmlError) -> Self { + Self::Xml { source } + } +} + pub struct TextXmlReader<'a> { xml_reader: Reader<&'a [u8]>, encoding: EncodingType, @@ -41,7 +111,7 @@ impl<'a> TextXmlReader<'a> { self.encoding } - fn parse_attribute(&self, key: &[u8], value: &[u8]) -> Result { + fn parse_attribute(&self, key: &[u8], value: &[u8]) -> Result { let mut value = BytesMut::from(value.to_vec()); // Add the trailing null byte that kbin has at the end of strings @@ -68,7 +138,7 @@ impl<'a> TextXmlReader<'a> { fn parse_attributes( &self, attrs: Attributes<'a>, - ) -> Result<(StandardType, usize, Option, Vec)> { + ) -> Result<(StandardType, usize, Option, Vec), TextReaderError> { let mut node_type = None; let mut count = 0; let mut size = None; @@ -113,7 +183,7 @@ impl<'a> TextXmlReader<'a> { Err(e) => { error!("Error reading attribute: {:?}", e); }, - } + }; } let node_type = match node_type { @@ -128,7 +198,10 @@ impl<'a> TextXmlReader<'a> { Ok((node_type, count, size, attributes)) } - fn handle_start(&self, e: BytesStart) -> Result<(NodeCollection, usize, Option)> { + fn handle_start( + &self, + e: BytesStart, + ) -> Result<(NodeCollection, usize, Option), TextReaderError> { let (node_type, count, size, attributes) = self.parse_attributes(e.attributes())?; let is_array = count > 0; @@ -156,7 +229,7 @@ impl<'a> TextXmlReader<'a> { definition: &mut NodeDefinition, count: usize, size: Option, - ) -> Result<()> { + ) -> Result<(), TextReaderError> { let data = event.unescaped()?; let data = match definition.node_type { StandardType::String | StandardType::NodeStart => { @@ -168,21 +241,24 @@ impl<'a> TextXmlReader<'a> { data.freeze() }, - _ => { + node_type => { let text = str::from_utf8(&*data)?; - let value = - Value::from_string(definition.node_type, text, definition.is_array, count)?; + let value = Value::from_string(node_type, text, definition.is_array, count) + .context(ValueDecode { node_type })?; + // The read number of bytes must match the size attribute, if set if let Value::Binary(data) = &value { - // The read number of bytes must match the size attribute, if set if let Some(size) = size { if data.len() != size { - return Err(KbinError::InvalidState.into()); + return Err(TextReaderError::MismatchedBinaryNodeLength { + len: data.len(), + size, + }); } } } - Bytes::from(value.to_bytes()?) + Bytes::from(value.to_bytes().context(ValueEncode { node_type })?) }, }; @@ -190,20 +266,22 @@ impl<'a> TextXmlReader<'a> { definition.node_type = StandardType::String; } - if let NodeData::Some { - ref mut value_data, .. - } = definition.data_mut() - { - *value_data = data; - } else { - // There should be a valid `NodeData` structure from the `Event::Start` handler - return Err(KbinError::InvalidState.into()); - } + match definition.data_mut() { + NodeData::Some { + ref mut value_data, .. + } => { + *value_data = data; + }, + NodeData::None => { + // There should be a valid `NodeData` structure from the `Event::Start` handler + return Err(TextReaderError::NoNodeData); + }, + }; Ok(()) } - pub fn as_node_collection(&mut self) -> Result> { + pub fn as_node_collection(&mut self) -> Result, TextReaderError> { // A buffer size for reading a `quick_xml::events::Event` that I pulled // out of my head. let mut buf = Vec::with_capacity(1024); @@ -245,7 +323,8 @@ impl<'a> TextXmlReader<'a> { }, Event::Decl(e) => { if let Some(encoding) = e.encoding() { - self.encoding = EncodingType::from_label(&encoding?)?; + self.encoding = + EncodingType::from_label(&encoding?).context(InvalidEncoding)?; } }, Event::Eof => break,