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");
assert!(matches!(
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]`.
///
/// This is the same as a `DeserializeError` with an additional variant to return any error yealded
/// by the inner bytes iterator.
#[derive(Debug)]
pub enum DecodeError<E> {
/// Attempted to decode an object from an iterator that yielded too many bytes.
TooManyBytes,
/// Invalid consensus encoding.
Consensus(Error),
Parse(ParseError),
/// Data unconsumed error.
Unconsumed,
/// Other decoding error.
Other(E),
Other(E), // Yielded by the inner iterator.
}
internals::impl_from_infallible!(DecodeError<E>);
@ -68,9 +71,8 @@ impl<E: fmt::Debug> fmt::Display for DecodeError<E> {
use DecodeError::*;
match *self {
TooManyBytes =>
write!(f, "attempted to decode object from an iterator that yielded too many bytes"),
Consensus(ref e) => write_err!(f, "invalid consensus encoding"; e),
Parse(ref e) => write_err!(f, "error parsing encoded object"; e),
Unconsumed => write!(f, "data not consumed entirely when deserializing"),
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::*;
match *self {
TooManyBytes => None,
Consensus(ref e) => Some(e),
Parse(ref e) => Some(e),
Unconsumed => None,
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>> {
let result = T::consensus_decode(&mut self);
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(_), 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_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_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::{Deserializer, Serializer};
use super::{Decodable, Encodable, Error, ParseError};
use super::{Decodable, Encodable, ParseError};
use crate::consensus::{DecodeError, IterReader};
/// Hex-encoding strategy
@ -387,10 +387,8 @@ where
fn unify(self) -> E {
match self {
DecodeError::Other(error) => error,
DecodeError::TooManyBytes => E::custom(format_args!("got more bytes than expected")),
DecodeError::Consensus(Error::Parse(e)) => consensus_error_into_serde(e),
DecodeError::Consensus(Error::Io(_)) =>
unreachable!("iterator never returns I/O error"),
DecodeError::Unconsumed => E::custom(format_args!("got more bytes than expected")),
DecodeError::Parse(e) => consensus_error_into_serde(e),
}
}
}
@ -402,10 +400,8 @@ where
fn into_de_error<DE: serde::de::Error>(self) -> DE {
match self {
DecodeError::Other(error) => error.into_de_error(),
DecodeError::TooManyBytes => DE::custom(format_args!("got more bytes than expected")),
DecodeError::Consensus(Error::Parse(e)) => consensus_error_into_serde(e),
DecodeError::Consensus(Error::Io(_)) =>
unreachable!("iterator never returns I/O error"),
DecodeError::Unconsumed => DE::custom(format_args!("got more bytes than expected")),
DecodeError::Parse(e) => consensus_error_into_serde(e),
}
}
}