qubit_codec_text/error/charset_decode_error_kind.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// =============================================================================
8use thiserror::Error;
9
10/// Classifies failures detected while decoding encoded units into Unicode text.
11#[derive(Clone, Copy, Debug, Eq, Error, PartialEq)]
12pub enum CharsetDecodeErrorKind {
13 /// The input units do not form a well-formed encoded sequence.
14 #[error("The encoded text sequence is malformed.")]
15 MalformedSequence {
16 /// Optional malformed raw value captured from the offending input
17 /// unit.
18 value: Option<u32>,
19 },
20
21 /// The requested input unit index is outside the input buffer.
22 #[error("The input unit index is outside the input buffer.")]
23 InvalidInputIndex {
24 /// Length of the input provided to the codec call.
25 input_len: usize,
26 },
27
28 /// The requested output character index is outside the output buffer.
29 #[error("The output character index is outside the output buffer.")]
30 InvalidOutputIndex {
31 /// Length of the output provided to the codec call.
32 output_len: usize,
33 },
34
35 /// The closed input ended before a complete character was available.
36 #[error(
37 "The encoded text sequence is incomplete (required {required} units, available {available} units)."
38 )]
39 IncompleteSequence {
40 /// Total units required to complete the current sequence.
41 required: usize,
42
43 /// Total units currently available from the start of the current
44 /// sequence.
45 available: usize,
46 },
47
48 /// The decoded numeric value is not a valid Unicode scalar value.
49 #[error(
50 "The decoded code point 0x{value:x} is not a valid Unicode scalar value."
51 )]
52 InvalidCodePoint {
53 /// Raw decoded code-point value.
54 value: u32,
55 },
56}
57
58impl CharsetDecodeErrorKind {
59 /// Returns the number of required input units for this kind, if available.
60 ///
61 /// # Returns
62 ///
63 /// - `Some(required)` for [`Self::IncompleteSequence`];
64 /// - `None` for all other variants.
65 #[must_use]
66 #[inline(always)]
67 pub const fn required(self) -> Option<usize> {
68 match self {
69 Self::IncompleteSequence { required, .. } => Some(required),
70 Self::MalformedSequence { .. }
71 | Self::InvalidInputIndex { .. }
72 | Self::InvalidOutputIndex { .. }
73 | Self::InvalidCodePoint { .. } => None,
74 }
75 }
76
77 /// Returns the number of currently available input units for this kind, if
78 /// available.
79 ///
80 /// # Returns
81 ///
82 /// - `Some(available)` for [`Self::IncompleteSequence`];
83 /// - `None` for all other variants.
84 #[must_use]
85 #[inline(always)]
86 pub const fn available(self) -> Option<usize> {
87 match self {
88 Self::IncompleteSequence { available, .. } => Some(available),
89 Self::MalformedSequence { .. }
90 | Self::InvalidInputIndex { .. }
91 | Self::InvalidOutputIndex { .. }
92 | Self::InvalidCodePoint { .. } => None,
93 }
94 }
95
96 /// Returns the raw malformed value associated with this decoding error, if
97 /// any.
98 ///
99 /// # Returns
100 ///
101 /// - `Some(value)` for [`Self::MalformedSequence`] when a specific
102 /// malformed unit value is available.
103 /// - `Some(value)` for [`Self::InvalidCodePoint`].
104 /// - `None` for other variants.
105 #[must_use]
106 #[inline(always)]
107 pub const fn value(self) -> Option<u32> {
108 match self {
109 Self::MalformedSequence { value } => value,
110 Self::InvalidCodePoint { value } => Some(value),
111 Self::IncompleteSequence { .. }
112 | Self::InvalidInputIndex { .. }
113 | Self::InvalidOutputIndex { .. } => None,
114 }
115 }
116
117 /// Returns the input length when this error comes from an invalid input
118 /// index.
119 ///
120 /// # Returns
121 ///
122 /// - `Some(input_len)` for [`Self::InvalidInputIndex`];
123 /// - `None` for other variants.
124 #[must_use]
125 #[inline(always)]
126 pub const fn input_len(self) -> Option<usize> {
127 match self {
128 Self::InvalidInputIndex { input_len } => Some(input_len),
129 Self::MalformedSequence { .. }
130 | Self::IncompleteSequence { .. }
131 | Self::InvalidOutputIndex { .. }
132 | Self::InvalidCodePoint { .. } => None,
133 }
134 }
135
136 /// Returns the output length when this error comes from an invalid output
137 /// index.
138 ///
139 /// # Returns
140 ///
141 /// - `Some(output_len)` for [`Self::InvalidOutputIndex`];
142 /// - `None` for other variants.
143 #[must_use]
144 #[inline(always)]
145 pub const fn output_len(self) -> Option<usize> {
146 match self {
147 Self::InvalidOutputIndex { output_len } => Some(output_len),
148 Self::MalformedSequence { .. }
149 | Self::InvalidInputIndex { .. }
150 | Self::IncompleteSequence { .. }
151 | Self::InvalidCodePoint { .. } => None,
152 }
153 }
154
155 /// Returns whether this kind represents incomplete input.
156 ///
157 /// # Returns
158 ///
159 /// Returns `true` for [`Self::IncompleteSequence`].
160 #[must_use]
161 #[inline(always)]
162 pub const fn is_incomplete(self) -> bool {
163 matches!(self, Self::IncompleteSequence { .. })
164 }
165
166 /// Returns incomplete-input details for this kind.
167 ///
168 /// # Returns
169 ///
170 /// Returns `Some((required, available))` for [`Self::IncompleteSequence`],
171 /// or `None` for all other variants.
172 #[must_use]
173 #[inline(always)]
174 pub const fn incomplete(self) -> Option<(usize, usize)> {
175 match self {
176 Self::IncompleteSequence {
177 required,
178 available,
179 } => Some((required, available)),
180 Self::MalformedSequence { .. }
181 | Self::InvalidInputIndex { .. }
182 | Self::InvalidOutputIndex { .. }
183 | Self::InvalidCodePoint { .. } => None,
184 }
185 }
186
187 /// Returns whether this kind represents malformed encoded input.
188 ///
189 /// # Returns
190 ///
191 /// Returns `true` for malformed sequences and invalid decoded scalar
192 /// values. Incomplete input is caller-owned tail data and is not
193 /// treated as malformed input by buffered charset decoders.
194 #[must_use]
195 #[inline(always)]
196 pub const fn is_malformed_input(self) -> bool {
197 matches!(
198 self,
199 Self::MalformedSequence { .. } | Self::InvalidCodePoint { .. }
200 )
201 }
202}