Remove the IO error from DecodeError

The `DecodeError` (badly named) consensus decodes an object from an
iterator that implements `Read`. The `Read` impl never returns a real IO
error, we use the `io::Error` to temporarily wrap the error returned by
the inner iterator and unwrap it in `IterReader::decode`. As such there
is no reason for the `DecodeError` to hold an `encode::Error`, it can
hold an `encode::ParseError`.

The value of this change is easily seen in the removal of calls to
`unreachable`.
This commit is contained in:
Tobin C. Harding 2024-10-22 12:39:07 +11:00
parent 713196be0d
commit bbffa3db43
No known key found for this signature in database
GPG Key ID: 40BF9E4C269D6607
4 changed files with 19 additions and 21 deletions

View File

@ -1164,7 +1164,7 @@ mod tests {
hex.push_str("abcdef"); hex.push_str("abcdef");
assert!(matches!( assert!(matches!(
deserialize_hex::<Transaction>(&hex).unwrap_err(), deserialize_hex::<Transaction>(&hex).unwrap_err(),
FromHexError::Decode(DecodeError::TooManyBytes) FromHexError::Decode(DecodeError::Unconsumed)
)); ));
} }
} }

View File

@ -51,14 +51,17 @@ impl From<ParseError> for DeserializeError {
} }
/// Error when consensus decoding from an `[IterReader]`. /// Error when consensus decoding from an `[IterReader]`.
///
/// This is the same as a `DeserializeError` with an additional variant to return any error yealded
/// by the inner bytes iterator.
#[derive(Debug)] #[derive(Debug)]
pub enum DecodeError<E> { pub enum DecodeError<E> {
/// Attempted to decode an object from an iterator that yielded too many bytes.
TooManyBytes,
/// Invalid consensus encoding. /// Invalid consensus encoding.
Consensus(Error), Parse(ParseError),
/// Data unconsumed error.
Unconsumed,
/// Other decoding error. /// Other decoding error.
Other(E), Other(E), // Yielded by the inner iterator.
} }
internals::impl_from_infallible!(DecodeError<E>); internals::impl_from_infallible!(DecodeError<E>);
@ -68,9 +71,8 @@ impl<E: fmt::Debug> fmt::Display for DecodeError<E> {
use DecodeError::*; use DecodeError::*;
match *self { match *self {
TooManyBytes => Parse(ref e) => write_err!(f, "error parsing encoded object"; e),
write!(f, "attempted to decode object from an iterator that yielded too many bytes"), Unconsumed => write!(f, "data not consumed entirely when deserializing"),
Consensus(ref e) => write_err!(f, "invalid consensus encoding"; e),
Other(ref other) => write!(f, "other decoding error: {:?}", other), Other(ref other) => write!(f, "other decoding error: {:?}", other),
} }
} }
@ -82,8 +84,8 @@ impl<E: fmt::Debug + std::error::Error + 'static> std::error::Error for DecodeEr
use DecodeError::*; use DecodeError::*;
match *self { match *self {
TooManyBytes => None, Parse(ref e) => Some(e),
Consensus(ref e) => Some(e), Unconsumed => None,
Other(ref e) => Some(e), Other(ref e) => Some(e),
} }
} }

View File

@ -38,12 +38,12 @@ impl<E: fmt::Debug, I: Iterator<Item = Result<u8, E>>> IterReader<E, I> {
fn decode<T: Decodable>(mut self) -> Result<T, DecodeError<E>> { fn decode<T: Decodable>(mut self) -> Result<T, DecodeError<E>> {
let result = T::consensus_decode(&mut self); let result = T::consensus_decode(&mut self);
match (result, self.error) { match (result, self.error) {
(Ok(_), None) if self.iterator.next().is_some() => Err(DecodeError::TooManyBytes), (Ok(_), None) if self.iterator.next().is_some() => Err(DecodeError::Unconsumed),
(Ok(value), None) => Ok(value), (Ok(value), None) => Ok(value),
(Ok(_), Some(error)) => panic!("{} silently ate the error: {:?}", core::any::type_name::<T>(), error), (Ok(_), Some(error)) => panic!("{} silently ate the error: {:?}", core::any::type_name::<T>(), error),
(Err(consensus::encode::Error::Io(io_error)), Some(de_error)) if io_error.kind() == io::ErrorKind::Other && io_error.get_ref().is_none() => Err(DecodeError::Other(de_error)), (Err(consensus::encode::Error::Io(io_error)), Some(de_error)) if io_error.kind() == io::ErrorKind::Other && io_error.get_ref().is_none() => Err(DecodeError::Other(de_error)),
(Err(consensus_error), None) => Err(DecodeError::Consensus(consensus_error)), (Err(consensus::encode::Error::Parse(parse_error)), None) => Err(DecodeError::Parse(parse_error)),
(Err(consensus::encode::Error::Io(io_error)), de_error) => panic!("unexpected IO error {:?} returned from {}::consensus_decode(), deserialization error: {:?}", io_error, core::any::type_name::<T>(), de_error), (Err(consensus::encode::Error::Io(io_error)), de_error) => panic!("unexpected IO error {:?} returned from {}::consensus_decode(), deserialization error: {:?}", io_error, core::any::type_name::<T>(), de_error),
(Err(consensus_error), Some(de_error)) => panic!("{} should've returned `Other` IO error because of deserialization error {:?} but it returned consensus error {:?} instead", core::any::type_name::<T>(), de_error, consensus_error), (Err(consensus_error), Some(de_error)) => panic!("{} should've returned `Other` IO error because of deserialization error {:?} but it returned consensus error {:?} instead", core::any::type_name::<T>(), de_error, consensus_error),
} }

View File

@ -17,7 +17,7 @@ use serde::de::{SeqAccess, Unexpected, Visitor};
use serde::ser::SerializeSeq; use serde::ser::SerializeSeq;
use serde::{Deserializer, Serializer}; use serde::{Deserializer, Serializer};
use super::{Decodable, Encodable, Error, ParseError}; use super::{Decodable, Encodable, ParseError};
use crate::consensus::{DecodeError, IterReader}; use crate::consensus::{DecodeError, IterReader};
/// Hex-encoding strategy /// Hex-encoding strategy
@ -387,10 +387,8 @@ where
fn unify(self) -> E { fn unify(self) -> E {
match self { match self {
DecodeError::Other(error) => error, DecodeError::Other(error) => error,
DecodeError::TooManyBytes => E::custom(format_args!("got more bytes than expected")), DecodeError::Unconsumed => E::custom(format_args!("got more bytes than expected")),
DecodeError::Consensus(Error::Parse(e)) => consensus_error_into_serde(e), DecodeError::Parse(e) => consensus_error_into_serde(e),
DecodeError::Consensus(Error::Io(_)) =>
unreachable!("iterator never returns I/O error"),
} }
} }
} }
@ -402,10 +400,8 @@ where
fn into_de_error<DE: serde::de::Error>(self) -> DE { fn into_de_error<DE: serde::de::Error>(self) -> DE {
match self { match self {
DecodeError::Other(error) => error.into_de_error(), DecodeError::Other(error) => error.into_de_error(),
DecodeError::TooManyBytes => DE::custom(format_args!("got more bytes than expected")), DecodeError::Unconsumed => DE::custom(format_args!("got more bytes than expected")),
DecodeError::Consensus(Error::Parse(e)) => consensus_error_into_serde(e), DecodeError::Parse(e) => consensus_error_into_serde(e),
DecodeError::Consensus(Error::Io(_)) =>
unreachable!("iterator never returns I/O error"),
} }
} }
} }