qubit-codec 0.16.0

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.
        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.
    ///
    /// # 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.
    ///
    /// # 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]
    #[must_use]
    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]
    #[must_use]
    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]
    #[must_use]
    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]
    #[must_use]
    pub const fn consumed_units(&self) -> Option<NonZeroUsize> {
        match self {
            Self::Invalid { consumed, .. } => *consumed,
            Self::Incomplete { .. } => None,
        }
    }
}