Skip to main content

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}