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}