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}