Skip to main content

qubit_codec_text/decode/
charset_decoder.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 qubit_codec::{
9    BufferedDecodeEngine,
10    BufferedDecoder,
11    BufferedTranscoder,
12    CapacityError,
13    FinishError,
14    TranscodeProgress,
15};
16
17use crate::{
18    CharsetCodec,
19    CharsetDecodeError,
20    MalformedAction,
21};
22
23use super::{
24    charset_decode_hooks::CharsetDecodeHooks,
25    charset_decode_policy::CharsetDecodePolicy,
26};
27
28/// Converts units of one charset into Unicode scalar values.
29///
30/// `CharsetDecoder` wraps a low-level [`CharsetCodec`] and applies the
31/// configured [`MalformedAction`] whenever the codec reports malformed input.
32/// The decoder asks the wrapped codec whether one value can be decoded from the
33/// currently available units. If the codec reports a valid incomplete prefix,
34/// the tail is left in the caller-provided input slice and
35/// [`crate::TranscodeStatus::NeedInput`] is returned. Callers must handle
36/// incomplete EOF tails before calling [`BufferedTranscoder::finish`].
37///
38/// # Type Parameters
39///
40/// - `C`: Low-level charset codec used to decode source storage units into one
41///   Unicode scalar value.
42#[derive(Clone, Debug, Eq, PartialEq)]
43pub struct CharsetDecoder<C>
44where
45    C: CharsetCodec,
46{
47    /// Common buffered decode engine.
48    engine: BufferedDecodeEngine<C, CharsetDecodeHooks>,
49    /// Public malformed-input policy metadata.
50    policy: CharsetDecodePolicy,
51}
52
53impl<C> CharsetDecoder<C>
54where
55    C: CharsetCodec,
56{
57    /// Creates a decoder with default replacement policy.
58    ///
59    /// # Parameters
60    ///
61    /// - `codec`: Low-level charset codec used to decode input units.
62    ///
63    /// # Returns
64    ///
65    /// Returns a decoder whose malformed action is [`MalformedAction::Replace`]
66    /// and whose replacement character is `U+FFFD`.
67    #[must_use]
68    #[inline(always)]
69    pub fn new(codec: C) -> Self {
70        Self::with_policy(codec, CharsetDecodePolicy::default())
71    }
72
73    /// Creates a decoder with an explicit malformed-input policy.
74    ///
75    /// # Parameters
76    ///
77    /// - `codec`: Low-level charset codec used to decode input units.
78    /// - `policy`: Malformed-input policy used by the decoder.
79    ///
80    /// # Returns
81    ///
82    /// Returns a decoder configured with `policy`.
83    #[must_use]
84    #[inline]
85    pub fn with_policy(codec: C, policy: CharsetDecodePolicy) -> Self {
86        let hooks = CharsetDecodeHooks::from_policy(policy);
87        Self {
88            engine: BufferedDecodeEngine::new(codec, hooks),
89            policy,
90        }
91    }
92
93    /// Returns the configured malformed-input action.
94    ///
95    /// # Returns
96    ///
97    /// Returns the action used when source input is malformed.
98    #[must_use]
99    #[inline(always)]
100    pub const fn malformed_action(&self) -> MalformedAction {
101        self.policy.malformed_action()
102    }
103
104    /// Returns the configured replacement character.
105    ///
106    /// # Returns
107    ///
108    /// Returns the character emitted when [`MalformedAction::Replace`] is used.
109    #[must_use]
110    #[inline(always)]
111    pub const fn replacement(&self) -> char {
112        self.policy.replacement()
113    }
114}
115
116impl<C> BufferedDecoder<C::Unit, char> for CharsetDecoder<C> where
117    C: CharsetCodec
118{
119}
120
121impl<C> BufferedTranscoder<C::Unit, char> for CharsetDecoder<C>
122where
123    C: CharsetCodec,
124{
125    type Error = CharsetDecodeError;
126
127    /// Returns the maximum number of characters decoded from `input_len` units.
128    #[inline(always)]
129    fn max_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
130        self.engine.max_output_len(input_len)
131    }
132
133    /// Returns the maximum number of characters emitted by finishing internal
134    /// state.
135    #[inline(always)]
136    fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
137        Ok(self.engine.max_finish_output_len())
138    }
139
140    /// Clears hook-owned state while keeping decoder policy.
141    #[inline(always)]
142    fn reset(&mut self) {
143        self.engine.reset();
144    }
145
146    /// Decodes source units into Unicode scalar values while applying malformed
147    /// policy.
148    #[inline(always)]
149    fn transcode(
150        &mut self,
151        input: &[C::Unit],
152        input_index: usize,
153        output: &mut [char],
154        output_index: usize,
155    ) -> Result<TranscodeProgress, Self::Error> {
156        self.engine
157            .transcode(input, input_index, output, output_index)
158    }
159
160    /// Finishes decoder-owned final output after EOF.
161    #[inline(always)]
162    fn finish(
163        &mut self,
164        output: &mut [char],
165        output_index: usize,
166    ) -> Result<usize, FinishError<Self::Error>> {
167        self.engine.finish(output, output_index)
168    }
169}