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#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
25pub enum DecodeFailure<E> {
26    /// The visible input is a valid prefix but not enough to decode a value.
27    #[non_exhaustive]
28    Incomplete {
29        /// Codec-specific context for the incomplete prefix, when available.
30        ///
31        /// Stream drivers may ignore this field while retrying input. Once the
32        /// stream reaches EOF, adapters can use it to preserve detailed
33        /// diagnostics instead of reconstructing an error from the length
34        /// bound alone.
35        source: Option<E>,
36        /// Current non-zero minimum total units required before retrying from
37        /// the value start.
38        ///
39        /// A later retry may raise this lower bound after the codec sees a
40        /// longer prefix.
41        required_total: NonZeroUsize,
42    },
43
44    /// The input is invalid for the codec.
45    #[non_exhaustive]
46    Invalid {
47        /// Codec-specific invalid-input error.
48        source: E,
49        /// Invalid units that may be consumed by a non-strict policy, when
50        /// known.
51        consumed: Option<NonZeroUsize>,
52    },
53}
54
55impl<E> DecodeFailure<E> {
56    /// Creates an incomplete-input decode failure.
57    ///
58    /// # Parameters
59    ///
60    /// - `required_total`: Current non-zero minimum total units required before
61    ///   retrying from the value start. A later retry may raise this lower
62    ///   bound.
63    ///
64    /// # Returns
65    ///
66    /// Returns an incomplete decode failure.
67    #[inline(always)]
68    #[must_use]
69    pub const fn incomplete(required_total: NonZeroUsize) -> Self {
70        Self::Incomplete {
71            source: None,
72            required_total,
73        }
74    }
75
76    /// Creates an incomplete-input decode failure with codec-specific context.
77    ///
78    /// # Parameters
79    ///
80    /// - `source`: Codec-specific error describing the visible incomplete
81    ///   prefix.
82    /// - `required_total`: Current non-zero minimum units required before
83    ///   retrying from the value start.
84    ///
85    /// # Returns
86    ///
87    /// Returns an incomplete decode failure retaining `source` for adapters
88    /// that need detailed EOF diagnostics.
89    #[inline(always)]
90    #[must_use]
91    pub const fn incomplete_with_source(source: E, required_total: NonZeroUsize) -> Self {
92        Self::Incomplete {
93            source: Some(source),
94            required_total,
95        }
96    }
97
98    /// Creates an invalid-input decode failure with a consumption hint.
99    ///
100    /// # Parameters
101    ///
102    /// - `source`: Codec-specific invalid-input error.
103    /// - `consumed`: Invalid input units that may be consumed.
104    ///
105    /// # Returns
106    ///
107    /// Returns an invalid decode failure.
108    #[inline(always)]
109    #[must_use]
110    pub const fn invalid(source: E, consumed: NonZeroUsize) -> Self {
111        Self::Invalid {
112            source,
113            consumed: Some(consumed),
114        }
115    }
116
117    /// Creates an invalid-input decode failure with unknown consumption.
118    ///
119    /// # Parameters
120    ///
121    /// - `source`: Codec-specific invalid-input error.
122    ///
123    /// # Returns
124    ///
125    /// Returns an invalid decode failure.
126    #[inline(always)]
127    #[must_use]
128    pub const fn invalid_unknown(source: E) -> Self {
129        Self::Invalid { source, consumed: None }
130    }
131
132    /// Returns the current minimum input length required before retrying an
133    /// incomplete prefix.
134    ///
135    /// # Returns
136    ///
137    /// Returns `Some(required_total)` for incomplete failures, where the value
138    /// is a lower bound that a later retry may raise, or `None` for
139    /// invalid-input failures.
140    #[inline(always)]
141    #[must_use]
142    pub const fn required_total(&self) -> Option<NonZeroUsize> {
143        match self {
144            Self::Incomplete { required_total, .. } => Some(*required_total),
145            Self::Invalid { .. } => None,
146        }
147    }
148
149    /// Borrows the codec-specific error retained for an incomplete prefix.
150    ///
151    /// # Returns
152    ///
153    /// Returns `Some(source)` when the codec supplied incomplete-input context,
154    /// or `None` when the failure only carries the retry bound.
155    #[inline(always)]
156    #[must_use]
157    pub const fn incomplete_source(&self) -> Option<&E> {
158        match self {
159            Self::Incomplete { source, .. } => source.as_ref(),
160            Self::Invalid { .. } => None,
161        }
162    }
163
164    /// Borrows the codec-specific invalid-input error.
165    ///
166    /// # Returns
167    ///
168    /// Returns `Some(source)` for invalid-input failures, or `None` for
169    /// incomplete failures.
170    #[inline(always)]
171    #[must_use]
172    pub const fn invalid_source(&self) -> Option<&E> {
173        match self {
174            Self::Invalid { source, .. } => Some(source),
175            Self::Incomplete { .. } => None,
176        }
177    }
178
179    /// Returns invalid units that may be consumed by a non-strict policy.
180    ///
181    /// # Returns
182    ///
183    /// Returns `Some(consumed)` for [`Invalid`](Self::Invalid), or `None` when
184    /// the failure is incomplete or invalid with unknown consumption.
185    #[inline(always)]
186    #[must_use]
187    pub const fn consumed_units(&self) -> Option<NonZeroUsize> {
188        match self {
189            Self::Invalid { consumed, .. } => *consumed,
190            Self::Incomplete { .. } => None,
191        }
192    }
193}