qubit_codec/codec/codec_decode_error.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2026 Haixing Hu.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10//! Generic decode error used by codec adapters.
11
12use thiserror::Error;
13
14/// Error reported by codec-backed value and buffered decoder adapters.
15///
16/// The wrapped codec remains responsible for domain-specific decode failures.
17/// This type adds adapter-level failures that cannot be represented by the
18/// wrapped codec itself, such as a value decoder receiving too few units before
19/// it can safely call [`crate::Codec::decode_unchecked`] or a buffered decoder
20/// receiving an invalid output start index.
21#[derive(Clone, Copy, Debug, Eq, Error, Hash, PartialEq)]
22pub enum CodecDecodeError<E> {
23 /// The wrapped codec reported a decode error.
24 #[error("codec decode error at input index {input_index}: {source}")]
25 Decode {
26 /// Error returned by the wrapped codec.
27 #[source]
28 source: E,
29 /// Absolute input index at which the adapter called the wrapped codec.
30 input_index: usize,
31 },
32
33 /// The adapter could not safely call the wrapped codec because input ended.
34 #[error("incomplete input at index {input_index}: required {required_total} units, available {available}")]
35 Incomplete {
36 /// Absolute input index where the incomplete value starts.
37 input_index: usize,
38 /// Total units required from `input_index`.
39 required_total: usize,
40 /// Units available from `input_index`.
41 available: usize,
42 },
43
44 /// A whole-value decode succeeded but left trailing input units.
45 #[error("trailing input after decoded value: consumed {consumed} units, remaining {remaining}")]
46 TrailingInput {
47 /// Units consumed by the decoded value.
48 consumed: usize,
49 /// Extra units left after the decoded value.
50 remaining: usize,
51 },
52
53 /// The caller supplied an input index outside the input slice.
54 #[error("invalid input index {index} for input length {len}")]
55 InvalidInputIndex {
56 /// Invalid input index supplied by the caller.
57 index: usize,
58 /// Length of the input slice.
59 len: usize,
60 },
61
62 /// The caller supplied an output index outside the output slice.
63 #[error("invalid output index {index} for output length {len}")]
64 InvalidOutputIndex {
65 /// Invalid output index supplied by the caller.
66 index: usize,
67 /// Length of the output slice.
68 len: usize,
69 },
70}
71
72impl<E> CodecDecodeError<E> {
73 /// Creates an error wrapping a codec-specific decode error.
74 ///
75 /// # Parameters
76 ///
77 /// - `source`: Error returned by the wrapped codec.
78 /// - `input_index`: Absolute input index used for the codec call.
79 ///
80 /// # Returns
81 ///
82 /// Returns a codec decode error wrapper.
83 #[must_use]
84 #[inline(always)]
85 pub const fn decode(source: E, input_index: usize) -> Self {
86 Self::Decode { source, input_index }
87 }
88
89 /// Creates an adapter-level incomplete-input error.
90 ///
91 /// # Parameters
92 ///
93 /// - `input_index`: Absolute input index where the incomplete value starts.
94 /// - `required_total`: Total units required from `input_index`.
95 /// - `available`: Units available from `input_index`.
96 ///
97 /// # Returns
98 ///
99 /// Returns an incomplete-input error.
100 #[must_use]
101 #[inline(always)]
102 pub const fn incomplete(input_index: usize, required_total: usize, available: usize) -> Self {
103 Self::Incomplete {
104 input_index,
105 required_total,
106 available,
107 }
108 }
109
110 /// Creates a trailing-input error for whole-value decoding.
111 ///
112 /// # Parameters
113 ///
114 /// - `consumed`: Units consumed by the decoded value.
115 /// - `remaining`: Extra units left after the decoded value.
116 ///
117 /// # Returns
118 ///
119 /// Returns a trailing-input error.
120 #[must_use]
121 #[inline(always)]
122 pub const fn trailing_input(consumed: usize, remaining: usize) -> Self {
123 Self::TrailingInput { consumed, remaining }
124 }
125
126 /// Creates an invalid-input-index error.
127 ///
128 /// # Parameters
129 ///
130 /// - `index`: Invalid input index supplied by the caller.
131 /// - `len`: Length of the input slice.
132 ///
133 /// # Returns
134 ///
135 /// Returns an invalid-input-index error.
136 #[must_use]
137 #[inline(always)]
138 pub const fn invalid_input_index(index: usize, len: usize) -> Self {
139 Self::InvalidInputIndex { index, len }
140 }
141
142 /// Creates an invalid-output-index error.
143 ///
144 /// # Parameters
145 ///
146 /// - `index`: Invalid output index supplied by the caller.
147 /// - `len`: Length of the output slice.
148 ///
149 /// # Returns
150 ///
151 /// Returns an invalid-output-index error.
152 #[must_use]
153 #[inline(always)]
154 pub const fn invalid_output_index(index: usize, len: usize) -> Self {
155 Self::InvalidOutputIndex { index, len }
156 }
157}