Skip to main content

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}