use rmp::Marker;
pub use rmp::decode::bytes::{Bytes, BytesReadError};
pub use rmp::decode::{
DecodeStringError, NumValueReadError, RmpRead, RmpReadErr, ValueReadError, read_array_len,
read_bin_len, read_bool, read_i8, read_i16, read_i32, read_i64, read_int, read_map_len,
read_str_from_slice, read_str_len, read_u8, read_u16, read_u32, read_u64,
};
#[derive(Debug, derive_more::Display, derive_more::From)]
#[display("could not decode MessagePack value: {_0:?}")]
pub enum DecodeError<'a, E: RmpReadErr = BytesReadError> {
DecodeString(DecodeStringError<'a, E>),
NumValueRead(NumValueReadError<E>),
ValueRead(ValueReadError<E>),
#[display("expected array of length {expected}, but got {actual}")]
WrongArrayLength {
expected: usize,
actual: u32,
},
#[display("{_0}")]
Custom(String),
}
impl<E: RmpReadErr> DecodeError<'_, E> {
pub fn into_static(self) -> DecodeError<'static, E> {
match self {
Self::DecodeString(e) => DecodeError::DecodeString(match e {
DecodeStringError::InvalidMarkerRead(e) => DecodeStringError::InvalidMarkerRead(e),
DecodeStringError::InvalidDataRead(e) => DecodeStringError::InvalidDataRead(e),
DecodeStringError::TypeMismatch(m) => DecodeStringError::TypeMismatch(m),
DecodeStringError::BufferSizeTooSmall(n) => {
DecodeStringError::BufferSizeTooSmall(n)
}
DecodeStringError::InvalidUtf8(_, e) => {
DecodeStringError::InvalidUtf8(b"[elided]", e)
}
}),
Self::NumValueRead(e) => DecodeError::NumValueRead(e),
Self::ValueRead(e) => DecodeError::ValueRead(e),
Self::WrongArrayLength { expected, actual } => {
DecodeError::WrongArrayLength { expected, actual }
}
Self::Custom(s) => DecodeError::Custom(s),
}
}
pub fn type_mismatch(&self) -> Option<Marker> {
match self {
Self::DecodeString(DecodeStringError::TypeMismatch(m)) => Some(*m),
Self::NumValueRead(NumValueReadError::TypeMismatch(m)) => Some(*m),
Self::ValueRead(ValueReadError::TypeMismatch(m)) => Some(*m),
_ => None,
}
}
}
impl<E: RmpReadErr> From<DecodeError<'_, E>> for eyre::Report {
fn from(e: DecodeError<'_, E>) -> Self {
eyre::eyre!("{e}")
}
}
pub fn read_string<'a>(bytes: &mut Bytes<'a>) -> Result<String, DecodeError<'a>> {
let slice = bytes.remaining_slice();
let (string, rest) = match read_str_from_slice(slice) {
Ok(pair) => pair,
Err(e) => {
if let DecodeStringError::TypeMismatch(_) = e {
bytes.read_u8().expect("TypeMismatch implies stream contains a marker byte");
}
return Err(e.into());
}
};
*bytes = Bytes::new(rest);
Ok(string.into())
}
pub fn with_str<'a, F, R>(bytes: &mut Bytes<'a>, f: F) -> Result<R, DecodeError<'a>>
where
F: FnOnce(&'a str) -> R,
{
let slice = bytes.remaining_slice();
let (string, rest) = read_str_from_slice(slice)?;
let result = f(string);
*bytes = Bytes::new(rest);
Ok(result)
}
pub fn read_optional<'a, R, T, F, E>(
reader: &mut R,
read: F,
) -> Result<Option<T>, DecodeError<'a, R::Error>>
where
R: RmpRead,
F: FnOnce(&mut R) -> Result<T, E>,
E: Into<DecodeError<'a, R::Error>>,
{
let err = match read(reader) {
Ok(v) => return Ok(Some(v)),
Err(e) => e.into(),
};
if let Some(Marker::Null) = err.type_mismatch() {
Ok(None)
} else {
Err(err)
}
}
pub fn read_array<'a, R, T, F, E>(
reader: &mut R,
read: F,
) -> impl Iterator<Item = Result<T, DecodeError<'a, R::Error>>>
where
R: RmpRead,
F: FnMut(&mut R) -> Result<T, E>,
E: Into<DecodeError<'a, R::Error>>,
{
read_array_impl(reader, read_array_len, read)
}
pub fn read_fixed_array<'a, const N: usize, R, T, F, E>(
reader: &mut R,
read: F,
) -> Result<[T; N], DecodeError<'a, R::Error>>
where
R: RmpRead,
F: FnMut(&mut R) -> Result<T, E>,
E: Into<DecodeError<'a, R::Error>>,
{
read_fixed_array_impl(reader, read_array_len, read)
}
pub fn read_binary_array<'a, R>(
reader: &mut R,
) -> impl Iterator<Item = Result<u8, DecodeError<'a, R::Error>>>
where
R: RmpRead,
{
read_array_impl(reader, read_bin_len, |reader| {
reader.read_u8().map_err(ValueReadError::InvalidDataRead)
})
}
pub fn read_fixed_binary_array<'a, const N: usize, R>(
reader: &mut R,
) -> Result<[u8; N], DecodeError<'a, R::Error>>
where
R: RmpRead,
{
read_fixed_array_impl(reader, read_bin_len, |reader| {
reader.read_u8().map_err(ValueReadError::InvalidDataRead)
})
}
fn read_array_impl<'a, R, T, L, F, E>(
reader: &mut R,
read_len: L,
mut read: F,
) -> impl Iterator<Item = Result<T, DecodeError<'a, R::Error>>>
where
R: RmpRead,
L: FnOnce(&mut R) -> Result<u32, ValueReadError<R::Error>>,
F: FnMut(&mut R) -> Result<T, E>,
E: Into<DecodeError<'a, R::Error>>,
{
let (len, error) = match read_len(reader) {
Ok(len) => (len, None),
Err(e) => (0, Some(e)),
};
let items = (0..len).map(move |_| read(reader).map_err(Into::into));
error.into_iter().map(|e| Err(e.into())).chain(items)
}
fn read_fixed_array_impl<'a, const N: usize, R, T, L, F, E>(
reader: &mut R,
read_len: L,
mut read: F,
) -> Result<[T; N], DecodeError<'a, R::Error>>
where
R: RmpRead,
L: FnOnce(&mut R) -> Result<u32, ValueReadError<R::Error>>,
F: FnMut(&mut R) -> Result<T, E>,
E: Into<DecodeError<'a, R::Error>>,
{
use std::mem::MaybeUninit;
let actual_len = read_len(reader)?;
if !u32::try_from(N).is_ok_and(|n| n == actual_len) {
return Err(DecodeError::WrongArrayLength {
expected: N,
actual: actual_len,
});
}
let mut array = MaybeUninit::<[T; N]>::uninit();
let array_of_uninit: &mut [MaybeUninit<T>; N] = array.as_mut();
for slot in array_of_uninit {
slot.write(read(reader).map_err(Into::into)?);
}
#[allow(
unsafe_code,
reason = "Doing this without unsafe code is much less efficient. We would have to create \
an
`[Option<T>; N]`, which could be up to twice the size, fill in each element, and then
use `array::map` to call `Option::unwrap` on each element. Besides the overhead of
`unwrap`, `array::map` is noted as being inefficient on large arrays."
)]
Ok(unsafe { array.assume_init() })
}