ufotofu 0.12.5

Abstractions for lazily consuming and producing sequences
Documentation
use crate::prelude::*;

use crate::ProduceAtLeastError;

use core::{
    convert::Infallible,
    fmt::{Debug, Display, Formatter},
};

use core::error::Error;

/// A trait for types which can be decoded from a sequence of `Symbol`s.
///
/// More precisely, this trait may be implemented by types that belong to an [encoding relation](super). The trait specifies (via the [`Decodable::decode`] method) how to decode values by reading a sequence of symbols from a [`BulkProducer`].
///
/// API contracts:
///
/// - The result of decoding must depend only on the decoded symbols, not on details of the producer such as when it yields or how many symbols it exposes at a time.
/// - `decode` must not read any symbols beyond the end of the encoding.
/// - Equal sequences of symbols must (deterministically) decode to equal values.
/// - For types that also implement [`Encodable`](super::Encodable) and [`Eq`], encoding a value and then decoding it must yield a value equal to the original.
///
/// ```
/// use ufotofu::codec_prelude::*;
/// use ufotofu::codec::endian::U32BE;
///
/// # pollster::block_on(async{
/// let mut pro = [0, 0, 1, 2, 99].into_producer();
/// assert_eq!(U32BE::decode(&mut pro).await?.0, 258);
/// # Result::<(), DecodeError::<(), Infallible, Infallible>>::Ok(())
/// # });
/// ```
///
/// <br/>Counterpart: the [`Encodable`](super::Encodable) trait.
pub trait Decodable<Symbol = u8>: Sized {
    /// Reason why decoding can fail (beyond an unexpected end of input or a producer error).
    type ErrorReason;

    /// Decodes the symbols produced by the given bulk producer into a `Self`, or yields an error if the producer does not produce a valid encoding.
    ///
    /// <br/>Counterpart: the [`Encodable::encode`](super::Encodable::encode) method.
    async fn decode<P>(
        producer: &mut P,
    ) -> Result<Self, DecodeError<P::Final, P::Error, Self::ErrorReason>>
    where
        P: BulkProducer<Item = Symbol> + ?Sized,
        Self: Sized;
}

/// Decoding for an [encoding relation](super) with a one-to-one mapping between values and their codes (i.e., the relation is a [bijection](https://en.wikipedia.org/wiki/Bijection)).
///
/// Implementations of this trait may specialise arbitrary encoding relations to a canonic subset.
///
/// API contracts:
///
/// - Two nonequal codes must not decode to equal values with `decode_canonic`.
/// - Any code that decodes via `decode_canonic` must also decode via `decode` to an equal value.
/// - For types that also implement [`Encodable`](super::Encodable) and [`Eq`], if canonically decoding a sequence of symbols succeeds, then reencoding the resulting value must result in the original sequence of symbols.
///
/// ```
/// use ufotofu::codec_prelude::*;
/// use ufotofu::codec::endian::U32BE;
///
/// # pollster::block_on(async{
/// let mut pro = [0, 0, 1, 2, 99].into_producer();
/// assert_eq!(U32BE::decode_canonic(&mut pro).await?.0, 258);
/// # Result::<(), DecodeError::<(), Infallible, Infallible>>::Ok(())
/// # });
/// ```
///
/// There is no corresponding `EncodableCanonic` trait, because [`Encodable`](super::Encodable) already fulfils the dual requirement of two nonequal values yielding nonequal codes.
pub trait DecodableCanonic<Symbol = u8>: Decodable<Symbol> {
    /// The type for reporting that the sequence of symbols to decode was not a valid canonic encoding of any value of type `Self`.
    ///
    /// Typically contains at least as much information as [`Self::ErrorReason`](Decodable::ErrorReason). If the encoding relation implemented by [`Decodable`] is already canonic, then [`ErrorCanonic`](DecodableCanonic::ErrorCanonic) should be equal to [`Self::ErrorReason`](Decodable::ErrorReason).
    type ErrorCanonic: From<Self::ErrorReason>;

    /// Decodes the symbols produced by the given bulk producer into a `Self`, and errors if the input encoding is not the canonical one.
    async fn decode_canonic<P>(
        producer: &mut P,
    ) -> Result<Self, DecodeError<P::Final, P::Error, Self::ErrorCanonic>>
    where
        P: BulkProducer<Item = Symbol> + ?Sized,
        Self: Sized;
}

/// The reasons why [decoding](Decodable::decode) can fail: the bulk producer might emit its final item too early, it might emit an error, or the symbols it emits might not be (prefixes of) correct codes.
///
/// `F` is the type of the [`Final`](crate::Producer::Final) value of the producer, `E` is the [`Error`](Producer::Error) type of the producer, and `Other` can describe in arbitrary detail why the decoded symbols were invalid.

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DecodeError<F, E, Other> {
    /// Could not decode a value because the producer of symbols emitted a [`Final`](crate::Producer::Final) value too early.
    UnexpectedEndOfInput(F),
    /// Could not decode a value because the producer of symbols emitted an [`Error`](crate::Producer::Error).
    ProducerError(E),
    /// Could not decode a value for reasons not related to the behaviour of the producer of symbols (except for the emitted symbols themselves).
    Other(Other),
}

impl<F, E, Other> DecodeError<F, E, Other> {
    /// Converts `self` with the given function if `self` is a [`DecodeError::Other`], returns `self` unchanged if it is a [`DecodeError::UnexpectedEndOfInput`] or [`DecodeError::ProducerError`].
    ///
    /// ```
    /// use ufotofu::codec_prelude::*;
    ///
    /// assert_eq!(
    ///     DecodeError::<u8, u8, u8>::Other(17).map_other(|x| x + 1),
    ///     DecodeError::<u8, u8, u8>::Other(18)
    /// );
    /// assert_eq!(
    ///     DecodeError::<u8, u8, u8>::UnexpectedEndOfInput(17).map_other(|x| x + 1),
    ///     DecodeError::<u8, u8, u8>::UnexpectedEndOfInput(17)
    /// );
    /// assert_eq!(
    ///     DecodeError::<u8, u8, u8>::ProducerError(17).map_other(|x| x + 1),
    ///     DecodeError::<u8, u8, u8>::ProducerError(17)
    /// );
    /// ```
    pub fn map_other<OtherB, Fun>(self, fun: Fun) -> DecodeError<F, E, OtherB>
    where
        Fun: FnOnce(Other) -> OtherB,
    {
        match self {
            DecodeError::Other(other) => DecodeError::Other(fun(other)),
            DecodeError::UnexpectedEndOfInput(fin) => DecodeError::UnexpectedEndOfInput(fin),
            DecodeError::ProducerError(err) => DecodeError::ProducerError(err),
        }
    }
}

impl<F, E, Other> From<E> for DecodeError<F, E, Other> {
    fn from(err: E) -> Self {
        DecodeError::ProducerError(err)
    }
}

impl<F, E, Other> From<ProduceAtLeastError<F, E>> for DecodeError<F, E, Other> {
    fn from(err: ProduceAtLeastError<F, E>) -> Self {
        match err.reason {
            Ok(fin) => DecodeError::UnexpectedEndOfInput(fin),
            Err(err) => DecodeError::ProducerError(err),
        }
    }
}

impl<F: Display, E: Display, Other: Display> Display for DecodeError<F, E, Other> {
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
        match self {
            DecodeError::ProducerError(err) => {
                write!(f, "Failed to decode a value because the producer of code symbols encountered an error: {err}",)
            }
            DecodeError::UnexpectedEndOfInput(fin) => {
                write!(
                    f,
                    "Failed to decode a value because the producer of code symbols emitted its final value unexpectedly early: {fin}"
                )
            }
            DecodeError::Other(reason) => {
                write!(f, "Failed to decode a value: {reason}")
            }
        }
    }
}

impl<F, E, Other> Error for DecodeError<F, E, Other>
where
    F: Display + Debug,
    E: 'static + Error,
    Other: 'static + Error,
{
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match self {
            DecodeError::ProducerError(err) => Some(err),
            DecodeError::UnexpectedEndOfInput(_fin) => None,
            DecodeError::Other(reason) => Some(reason),
        }
    }
}

/// An error reason for minimalistic decoding error handling: only tracks whether decoding failed because of invalid input or because of limitations of the decoder implementation.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Blame {
    /// Received an incorrect encoding.
    TheirFault,
    /// Received a valid encoding which we couldn't handle. Typical reasons include values that do not fit into a `usize`, or running out of memory.
    OurFault,
}

impl Blame {
    /// Converts from a `u64` to a `usize`, yielding a `DecodeError::Other(Blame::OurFault)` if the number does not fit into a `usize`.
    pub fn u64_to_usize<F, E>(n: u64) -> Result<usize, DecodeError<F, E, Blame>> {
        usize::try_from(n).map_err(|_| DecodeError::Other(Blame::OurFault))
    }

    /// Converts from a `u32` to a `usize`, yielding a `DecodeError::Other(Blame::OurFault)` if the number does not fit into a `usize`.
    pub fn u32_to_usize<F, E>(n: u32) -> Result<usize, DecodeError<F, E, Blame>> {
        usize::try_from(n).map_err(|_| DecodeError::Other(Blame::OurFault))
    }

    /// Converts from an `i64` to a `isize`, yielding a `DecodeError::Other(Blame::OurFault)` if the number does not fit into a `isize`.
    pub fn i64_to_usize<F, E>(n: i64) -> Result<isize, DecodeError<F, E, Blame>> {
        isize::try_from(n).map_err(|_| DecodeError::Other(Blame::OurFault))
    }

    /// Converts from an `i32` to a `isize`, yielding a `DecodeError::Other(Blame::OurFault)` if the number does not fit into a `isize`.
    pub fn i32_to_usize<F, E>(n: i32) -> Result<isize, DecodeError<F, E, Blame>> {
        isize::try_from(n).map_err(|_| DecodeError::Other(Blame::OurFault))
    }
}

impl From<Infallible> for Blame {
    fn from(_value: Infallible) -> Self {
        unreachable!()
    }
}

impl Display for Blame {
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
        match self {
            Blame::TheirFault => {
                write!(f, "Received an incorrect encoding.")
            }
            Blame::OurFault => {
                write!(
                    f,
                    "Received a correct encoding which we could not process for some reason."
                )
            }
        }
    }
}

impl Error for Blame {}