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(
92        source: E,
93        required_total: NonZeroUsize,
94    ) -> Self {
95        Self::Incomplete {
96            source: Some(source),
97            required_total,
98        }
99    }
100
101    /// Creates an invalid-input decode failure with a consumption hint.
102    ///
103    /// # Parameters
104    ///
105    /// - `source`: Codec-specific invalid-input error.
106    /// - `consumed`: Invalid input units that may be consumed.
107    ///
108    /// # Returns
109    ///
110    /// Returns an invalid decode failure.
111    #[inline(always)]
112    #[must_use]
113    pub const fn invalid(source: E, consumed: NonZeroUsize) -> Self {
114        Self::Invalid {
115            source,
116            consumed: Some(consumed),
117        }
118    }
119
120    /// Creates an invalid-input decode failure with unknown consumption.
121    ///
122    /// # Parameters
123    ///
124    /// - `source`: Codec-specific invalid-input error.
125    ///
126    /// # Returns
127    ///
128    /// Returns an invalid decode failure.
129    #[inline(always)]
130    #[must_use]
131    pub const fn invalid_unknown(source: E) -> Self {
132        Self::Invalid {
133            source,
134            consumed: None,
135        }
136    }
137
138    /// Returns the current minimum input length required before retrying an
139    /// incomplete prefix.
140    ///
141    /// # Returns
142    ///
143    /// Returns `Some(required_total)` for incomplete failures, where the value
144    /// is a lower bound that a later retry may raise, or `None` for
145    /// invalid-input failures.
146    #[inline(always)]
147    #[must_use]
148    pub const fn required_total(&self) -> Option<NonZeroUsize> {
149        match self {
150            Self::Incomplete { required_total, .. } => Some(*required_total),
151            Self::Invalid { .. } => None,
152        }
153    }
154
155    /// Borrows the codec-specific error retained for an incomplete prefix.
156    ///
157    /// # Returns
158    ///
159    /// Returns `Some(source)` when the codec supplied incomplete-input context,
160    /// or `None` when the failure only carries the retry bound.
161    #[inline(always)]
162    #[must_use]
163    pub const fn incomplete_source(&self) -> Option<&E> {
164        match self {
165            Self::Incomplete { source, .. } => source.as_ref(),
166            Self::Invalid { .. } => None,
167        }
168    }
169
170    /// Borrows the codec-specific invalid-input error.
171    ///
172    /// # Returns
173    ///
174    /// Returns `Some(source)` for invalid-input failures, or `None` for
175    /// incomplete failures.
176    #[inline(always)]
177    #[must_use]
178    pub const fn invalid_source(&self) -> Option<&E> {
179        match self {
180            Self::Invalid { source, .. } => Some(source),
181            Self::Incomplete { .. } => None,
182        }
183    }
184
185    /// Returns invalid units that may be consumed by a non-strict policy.
186    ///
187    /// # Returns
188    ///
189    /// Returns `Some(consumed)` for [`Invalid`](Self::Invalid), or `None` when
190    /// the failure is incomplete or invalid with unknown consumption.
191    #[inline(always)]
192    #[must_use]
193    pub const fn consumed_units(&self) -> Option<NonZeroUsize> {
194        match self {
195            Self::Invalid { consumed, .. } => *consumed,
196            Self::Incomplete { .. } => None,
197        }
198    }
199}