Skip to main content

qubit_codec/codec/
decode_failure.rs

1// =============================================================================
2//    Copyright (c) 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! Failures returned by low-level codec decode operations.
9
10use core::num::NonZeroUsize;
11
12/// Failure reported by [`crate::Codec::decode`].
13///
14/// This type separates stream-control failures from codec-domain failures.
15/// [`Incomplete`](Self::Incomplete) tells buffered adapters to preserve the
16/// current input tail and request more units. [`Invalid`](Self::Invalid)
17/// carries the codec-specific malformed, non-canonical, or otherwise invalid
18/// input error with a known non-zero consumption length, or with unknown
19/// consumption when set to `None`.
20///
21/// # Type Parameters
22///
23/// - `E`: Codec-specific invalid-input error type.
24///
25/// # Examples
26///
27/// ```
28/// use core::num::NonZeroUsize;
29///
30/// use qubit_codec::DecodeFailure;
31///
32/// let failure = DecodeFailure::<()>::incomplete(NonZeroUsize::MIN);
33/// assert_eq!(failure.required_total(), Some(NonZeroUsize::MIN));
34/// assert_eq!(failure.invalid_source(), None);
35/// ```
36#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
37#[must_use]
38pub enum DecodeFailure<E> {
39    /// The visible input is a valid prefix but not enough to decode a value.
40    #[non_exhaustive]
41    Incomplete {
42        /// Codec-specific context for the incomplete prefix, when available.
43        ///
44        /// Stream drivers may ignore this field while retrying input. Once the
45        /// stream reaches EOF, adapters can use it to preserve detailed
46        /// diagnostics instead of reconstructing an error from the length
47        /// bound alone.
48        source: Option<E>,
49        /// Current non-zero minimum total units required before retrying from
50        /// the value start.
51        ///
52        /// A later retry may raise this lower bound after the codec sees a
53        /// longer prefix.
54        required_total: NonZeroUsize,
55    },
56
57    /// The input is invalid for the codec.
58    #[non_exhaustive]
59    Invalid {
60        /// Codec-specific invalid-input error.
61        source: E,
62        /// Invalid units that may be consumed by a non-strict policy, when
63        /// known.
64        consumed: Option<NonZeroUsize>,
65    },
66}
67
68impl<E> DecodeFailure<E> {
69    /// Creates an incomplete-input decode failure.
70    ///
71    /// # Parameters
72    ///
73    /// - `required_total`: Current non-zero minimum total units required before
74    ///   retrying from the value start. A later retry may raise this lower
75    ///   bound.
76    ///
77    /// # Returns
78    ///
79    /// Returns an incomplete decode failure.
80    #[inline]
81    pub const fn incomplete(required_total: NonZeroUsize) -> Self {
82        Self::Incomplete {
83            source: None,
84            required_total,
85        }
86    }
87
88    /// Creates an incomplete-input decode failure with codec-specific context.
89    ///
90    /// # Parameters
91    ///
92    /// - `source`: Codec-specific error describing the visible incomplete
93    ///   prefix.
94    /// - `required_total`: Current non-zero minimum units required before
95    ///   retrying from the value start.
96    ///
97    /// # Returns
98    ///
99    /// Returns an incomplete decode failure retaining `source` for adapters
100    /// that need detailed EOF diagnostics.
101    #[inline]
102    pub const fn incomplete_with_source(source: E, required_total: NonZeroUsize) -> Self {
103        Self::Incomplete {
104            source: Some(source),
105            required_total,
106        }
107    }
108
109    /// Creates an invalid-input decode failure with a consumption hint.
110    ///
111    /// # Parameters
112    ///
113    /// - `source`: Codec-specific invalid-input error.
114    /// - `consumed`: Invalid input units that may be consumed.
115    ///
116    /// # Returns
117    ///
118    /// Returns an invalid decode failure.
119    #[inline]
120    pub const fn invalid(source: E, consumed: NonZeroUsize) -> Self {
121        Self::Invalid {
122            source,
123            consumed: Some(consumed),
124        }
125    }
126
127    /// Creates an invalid-input decode failure with unknown consumption.
128    ///
129    /// # Parameters
130    ///
131    /// - `source`: Codec-specific invalid-input error.
132    ///
133    /// # Returns
134    ///
135    /// Returns an invalid decode failure.
136    #[inline]
137    pub const fn invalid_unknown(source: E) -> Self {
138        Self::Invalid { source, consumed: None }
139    }
140
141    /// Returns the current minimum input length required before retrying an
142    /// incomplete prefix.
143    ///
144    /// # Returns
145    ///
146    /// Returns `Some(required_total)` for incomplete failures, where the value
147    /// is a lower bound that a later retry may raise, or `None` for
148    /// invalid-input failures.
149    #[inline]
150    #[must_use]
151    pub const fn required_total(&self) -> Option<NonZeroUsize> {
152        match self {
153            Self::Incomplete { required_total, .. } => Some(*required_total),
154            Self::Invalid { .. } => None,
155        }
156    }
157
158    /// Borrows the codec-specific error retained for an incomplete prefix.
159    ///
160    /// # Returns
161    ///
162    /// Returns `Some(source)` when the codec supplied incomplete-input context,
163    /// or `None` when the failure only carries the retry bound or is invalid.
164    #[inline]
165    #[must_use]
166    pub const fn incomplete_source(&self) -> Option<&E> {
167        match self {
168            Self::Incomplete { source, .. } => source.as_ref(),
169            Self::Invalid { .. } => None,
170        }
171    }
172
173    /// Borrows the codec-specific invalid-input error.
174    ///
175    /// # Returns
176    ///
177    /// Returns `Some(source)` for invalid-input failures, or `None` for
178    /// incomplete failures.
179    #[inline]
180    #[must_use]
181    pub const fn invalid_source(&self) -> Option<&E> {
182        match self {
183            Self::Invalid { source, .. } => Some(source),
184            Self::Incomplete { .. } => None,
185        }
186    }
187
188    /// Returns invalid units that may be consumed by a non-strict policy.
189    ///
190    /// # Returns
191    ///
192    /// Returns `Some(consumed)` for [`Invalid`](Self::Invalid), or `None` when
193    /// the failure is incomplete or invalid with unknown consumption.
194    #[inline]
195    #[must_use]
196    pub const fn consumed_units(&self) -> Option<NonZeroUsize> {
197        match self {
198            Self::Invalid { consumed, .. } => *consumed,
199            Self::Incomplete { .. } => None,
200        }
201    }
202}