use bytes::{Buf, TryGetError};
use thiserror::Error;
#[derive(Clone, Debug, Error, PartialEq, Eq, Hash)]
pub enum DecError {
#[error("unexpectedly encountered end of payload")]
UnexpectedEnd,
#[error("a varuint was not encoded in canonical form")]
VarUintNotCanonical,
#[error("a container was longer than allowed")]
ContainerTooLong,
#[error("trailing rubbish following the payload")]
TrailingRubbish,
#[cfg(all(feature = "alloc", feature = "validation"))]
#[error("validation error at {path}: [{rule_code}] {rule_message}")]
Validation {
path: String,
rule_code: &'static str,
rule_message: &'static str,
},
#[error("overran the extension of an extensible type")]
ExtensionOverran,
#[error("invalid union variant")]
InvalidUnionVariant,
#[error("utf-8 decoding failed")]
Utf8Decode,
#[error("there were duplicate keys in a map")]
DuplicateMapKeys,
#[error("float not finite")]
FloatNotFinite,
#[error("attempt to decode 'Infallible' payload")]
Never,
#[error("interoperability integer type is invalid")]
InteropIntInvalid,
}
impl From<TryGetError> for DecError {
fn from(_value: TryGetError) -> Self {
DecError::UnexpectedEnd
}
}
pub struct Decoder<B: MeasureBuf> {
bit_stash_remaining: u8,
bit_stash: u8,
pub buf: B,
}
impl<B: MeasureBuf> Decoder<B> {
pub fn new(buf: B) -> Self {
Self {
bit_stash_remaining: 0,
bit_stash: 0,
buf,
}
}
pub fn read_stash_bit(&mut self) -> Result<bool, DecError> {
if self.bit_stash_remaining == 0 {
self.bit_stash = self.buf.try_get_u8()?;
self.bit_stash_remaining = 8;
}
self.bit_stash_remaining -= 1;
let result = self.bit_stash & (1 << self.bit_stash_remaining) != 0;
Ok(result)
}
pub fn discard_bit_stash(&mut self) {
self.bit_stash_remaining = 0;
}
}
pub trait MeasureBuf: Buf {
fn position_measure(&self) -> u32;
}
impl MeasureBuf for &[u8] {
fn position_measure(&self) -> u32 {
u32::MAX - u32::try_from(self.len()).expect("buf too large")
}
}
#[cfg(feature = "alloc")]
pub fn decode_unvalidated<T: crate::codec::Compact1Codec>(bytes: &[u8]) -> Result<T, DecError> {
let mut decoder = Decoder::new(bytes);
let out = T::decode(&mut decoder)?;
if !decoder.buf.is_empty() {
return Err(DecError::TrailingRubbish);
}
Ok(out)
}
#[cfg(all(feature = "alloc", feature = "validation"))]
pub fn decode<T: crate::codec::Compact1Codec + oxidef_validation::Validate>(
bytes: &[u8],
) -> Result<T, DecError> {
let out: T = decode_unvalidated(bytes)?;
out.validate().map_err(|err| DecError::Validation {
path: format!("{:?}", err.path),
rule_code: err.rule_code,
rule_message: err.rule_message,
})?;
Ok(out)
}