qubit-codec 0.17.4

Core codec traits and buffer conversion primitives for Rust
Documentation
// =============================================================================
//    Copyright (c) 2026 Haixing Hu.
//
//    SPDX-License-Identifier: Apache-2.0
//
//    Licensed under the Apache License, Version 2.0.
// =============================================================================
//! Failures returned by low-level codec decode operations.

use core::num::NonZeroUsize;

/// Failure reported by [`crate::Codec::decode`].
///
/// This type separates stream-control failures from codec-domain failures.
/// [`Incomplete`](Self::Incomplete) tells buffered adapters to preserve the
/// current input tail and request more units. [`Invalid`](Self::Invalid)
/// carries the codec-specific malformed, non-canonical, or otherwise invalid
/// input error with a known non-zero consumption length, or with unknown
/// consumption when set to `None`.
///
/// # Type Parameters
///
/// - `E`: Codec-specific invalid-input error type.
///
/// # Examples
///
/// ```
/// use core::num::NonZeroUsize;
///
/// use qubit_codec::DecodeFailure;
///
/// let failure = DecodeFailure::<()>::incomplete(NonZeroUsize::MIN);
/// assert_eq!(failure.required_total(), Some(NonZeroUsize::MIN));
/// assert_eq!(failure.invalid_source(), None);
/// ```
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[must_use]
pub enum DecodeFailure<E> {
    /// The visible input is a valid prefix but not enough to decode a value.
    #[non_exhaustive]
    Incomplete {
        /// Codec-specific context for the incomplete prefix, when available.
        ///
        /// Stream drivers may ignore this field while retrying input. Once the
        /// stream reaches EOF, adapters can use it to preserve detailed
        /// diagnostics instead of reconstructing an error from the length
        /// bound alone.
        source: Option<E>,
        /// Current non-zero minimum total units required before retrying from
        /// the value start.
        ///
        /// A later retry may raise this lower bound after the codec sees a
        /// longer prefix.
        ///
        /// When returned by `Codec::decode` or `Codec::decode_eof`, this must
        /// be greater than the number of units currently available from the
        /// value start and no greater than
        /// `Codec::MAX_DECODE_UNITS_PER_VALUE`. It is a minimum total length
        /// from that start, not an additional-unit count; for example, with
        /// two units available and at least one more needed, report three.
        required_total: NonZeroUsize,
    },

    /// The input is invalid for the codec.
    #[non_exhaustive]
    Invalid {
        /// Codec-specific invalid-input error.
        source: E,
        /// Invalid units that may be consumed by a non-strict policy, when
        /// known.
        consumed: Option<NonZeroUsize>,
    },
}

impl<E> DecodeFailure<E> {
    /// Creates an incomplete-input decode failure.
    ///
    /// # Parameters
    ///
    /// - `required_total`: Current non-zero minimum total units required before
    ///   retrying from the value start. A later retry may raise this lower
    ///   bound. When returned by `Codec::decode` or `Codec::decode_eof`, it
    ///   must be greater than the units currently available from that start and
    ///   no greater than `Codec::MAX_DECODE_UNITS_PER_VALUE`. It counts total
    ///   units from the value start, not additional units to append. This
    ///   constructor has no input or Codec context and cannot validate those
    ///   conditions; the Codec implementation is responsible for them when
    ///   returning the failure.
    ///
    /// # Returns
    ///
    /// Returns an incomplete decode failure.
    #[inline]
    pub const fn incomplete(required_total: NonZeroUsize) -> Self {
        Self::Incomplete {
            source: None,
            required_total,
        }
    }

    /// Creates an incomplete-input decode failure with codec-specific context.
    ///
    /// # Parameters
    ///
    /// - `source`: Codec-specific error describing the visible incomplete
    ///   prefix.
    /// - `required_total`: Current non-zero minimum units required before
    ///   retrying from the value start. When returned by `Codec::decode` or
    ///   `Codec::decode_eof`, it must be greater than the units currently
    ///   available from that start and no greater than
    ///   `Codec::MAX_DECODE_UNITS_PER_VALUE`. It counts total units from the
    ///   value start, not additional units to append. This constructor has no
    ///   input or Codec context and cannot validate those conditions; the Codec
    ///   implementation is responsible for them when returning the failure.
    ///
    /// # Returns
    ///
    /// Returns an incomplete decode failure retaining `source` for adapters
    /// that need detailed EOF diagnostics.
    #[inline]
    pub const fn incomplete_with_source(source: E, required_total: NonZeroUsize) -> Self {
        Self::Incomplete {
            source: Some(source),
            required_total,
        }
    }

    /// Creates an invalid-input decode failure with a consumption hint.
    ///
    /// # Parameters
    ///
    /// - `source`: Codec-specific invalid-input error.
    /// - `consumed`: Invalid input units that may be consumed.
    ///
    /// # Returns
    ///
    /// Returns an invalid decode failure.
    #[inline]
    pub const fn invalid(source: E, consumed: NonZeroUsize) -> Self {
        Self::Invalid {
            source,
            consumed: Some(consumed),
        }
    }

    /// Creates an invalid-input decode failure with unknown consumption.
    ///
    /// # Parameters
    ///
    /// - `source`: Codec-specific invalid-input error.
    ///
    /// # Returns
    ///
    /// Returns an invalid decode failure.
    #[inline]
    pub const fn invalid_unknown(source: E) -> Self {
        Self::Invalid { source, consumed: None }
    }

    /// Returns the current minimum input length required before retrying an
    /// incomplete prefix.
    ///
    /// # Returns
    ///
    /// Returns `Some(required_total)` for incomplete failures, where the value
    /// is a lower bound that a later retry may raise, or `None` for
    /// invalid-input failures.
    #[inline]
    pub const fn required_total(&self) -> Option<NonZeroUsize> {
        match self {
            Self::Incomplete { required_total, .. } => Some(*required_total),
            Self::Invalid { .. } => None,
        }
    }

    /// Borrows the codec-specific error retained for an incomplete prefix.
    ///
    /// # Returns
    ///
    /// Returns `Some(source)` when the codec supplied incomplete-input context,
    /// or `None` when the failure only carries the retry bound or is invalid.
    #[inline]
    pub const fn incomplete_source(&self) -> Option<&E> {
        match self {
            Self::Incomplete { source, .. } => source.as_ref(),
            Self::Invalid { .. } => None,
        }
    }

    /// Borrows the codec-specific invalid-input error.
    ///
    /// # Returns
    ///
    /// Returns `Some(source)` for invalid-input failures, or `None` for
    /// incomplete failures.
    #[inline]
    pub const fn invalid_source(&self) -> Option<&E> {
        match self {
            Self::Invalid { source, .. } => Some(source),
            Self::Incomplete { .. } => None,
        }
    }

    /// Returns invalid units that may be consumed by a non-strict policy.
    ///
    /// # Returns
    ///
    /// Returns `Some(consumed)` for [`Invalid`](Self::Invalid), or `None` when
    /// the failure is incomplete or invalid with unknown consumption.
    #[inline]
    pub const fn consumed_units(&self) -> Option<NonZeroUsize> {
        match self {
            Self::Invalid { consumed, .. } => *consumed,
            Self::Incomplete { .. } => None,
        }
    }
}