qubit_codec_text/encode/
charset_encoder.rs1use core::fmt;
9
10use qubit_codec::{
11 BufferedEncodeEngine,
12 BufferedEncoder,
13 BufferedTranscoder,
14 CapacityError,
15 FinishError,
16 TranscodeProgress,
17};
18
19use crate::{
20 CharsetEncodeError,
21 UnmappableAction,
22};
23
24use super::{
25 charset_encode_hooks::{
26 CharsetEncodeHooks,
27 replacement_len,
28 },
29 charset_encode_policy::CharsetEncodePolicy,
30 charset_encode_probe::CharsetEncodeProbe,
31};
32
33#[derive(Clone)]
44pub struct CharsetEncoder<C>
45where
46 C: CharsetEncodeProbe,
47{
48 engine: BufferedEncodeEngine<C, CharsetEncodeHooks<C::Unit>>,
50 policy: CharsetEncodePolicy,
52 replacement_units_len: usize,
54}
55
56impl<C> CharsetEncoder<C>
57where
58 C: CharsetEncodeProbe,
59{
60 #[must_use]
84 pub fn new(codec: C) -> Self {
85 let policy = CharsetEncodePolicy::default();
86 match Self::create_hooks(&codec, policy) {
87 Ok((hooks, replacement_units_len)) => Self {
88 engine: BufferedEncodeEngine::new(codec, hooks),
89 policy,
90 replacement_units_len,
91 },
92 Err(default_error) => {
93 let fallback_policy = CharsetEncodePolicy::replace(
94 CharsetEncodePolicy::DEFAULT_FALLBACK_REPLACEMENT,
95 );
96 match Self::create_hooks(&codec, fallback_policy) {
97 Ok((hooks, replacement_units_len)) => Self {
98 engine: BufferedEncodeEngine::new(codec, hooks),
99 policy: fallback_policy,
100 replacement_units_len,
101 },
102 Err(_) => panic!(
103 "cannot initialize CharsetEncoder for {:?}: neither {:?} nor {:?} is encodable ({default_error})",
104 codec.charset(),
105 CharsetEncodePolicy::DEFAULT_REPLACEMENT,
106 CharsetEncodePolicy::DEFAULT_FALLBACK_REPLACEMENT,
107 ),
108 }
109 }
110 }
111 }
112
113 pub fn with_policy(
120 codec: C,
121 policy: CharsetEncodePolicy,
122 ) -> Result<Self, CharsetEncodeError> {
123 let (hooks, replacement_units_len) =
124 Self::create_hooks(&codec, policy)?;
125 Ok(Self {
126 engine: BufferedEncodeEngine::new(codec, hooks),
127 policy,
128 replacement_units_len,
129 })
130 }
131
132 #[must_use]
139 #[inline(always)]
140 pub const fn unmappable_action(&self) -> UnmappableAction {
141 self.policy.unmappable_action()
142 }
143
144 #[must_use]
151 #[inline(always)]
152 pub const fn replacement(&self) -> char {
153 self.policy.replacement()
154 }
155
156 pub(crate) fn create_hooks(
158 codec: &C,
159 policy: CharsetEncodePolicy,
160 ) -> Result<(CharsetEncodeHooks<C::Unit>, usize), CharsetEncodeError> {
161 let mut hooks = CharsetEncodeHooks::new(
162 policy.unmappable_action(),
163 policy.replacement(),
164 );
165 if policy.unmappable_action() != UnmappableAction::Replace {
166 return Ok((hooks, 0));
167 }
168 let replacement_units_len =
169 replacement_len(codec, policy.replacement())?;
170 hooks.set_replacement_units_len(replacement_units_len);
171 Ok((hooks, replacement_units_len))
172 }
173}
174
175impl<C> BufferedTranscoder<char, C::Unit> for CharsetEncoder<C>
176where
177 C: CharsetEncodeProbe,
178{
179 type Error = CharsetEncodeError;
180
181 #[inline(always)]
184 fn max_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
185 self.engine.max_output_len(input_len)
186 }
187
188 #[inline(always)]
190 fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
191 Ok(self.engine.max_finish_output_len())
192 }
193
194 #[inline(always)]
196 fn reset(&mut self) {
197 self.engine.reset();
198 }
199
200 #[inline(always)]
203 fn transcode(
204 &mut self,
205 input: &[char],
206 input_index: usize,
207 output: &mut [C::Unit],
208 output_index: usize,
209 ) -> Result<TranscodeProgress, Self::Error> {
210 self.engine
211 .transcode(input, input_index, output, output_index)
212 }
213
214 #[inline(always)]
216 fn finish(
217 &mut self,
218 output: &mut [C::Unit],
219 output_index: usize,
220 ) -> Result<usize, FinishError<Self::Error>> {
221 self.engine.finish(output, output_index)
222 }
223}
224
225impl<C> BufferedEncoder<char, C::Unit> for CharsetEncoder<C> where
226 C: CharsetEncodeProbe
227{
228}
229
230impl<C> Eq for CharsetEncoder<C> where C: CharsetEncodeProbe + Eq {}
231
232impl<C> PartialEq for CharsetEncoder<C>
233where
234 C: CharsetEncodeProbe + PartialEq,
235{
236 #[inline(always)]
238 fn eq(&self, other: &Self) -> bool {
239 self.engine == other.engine && self.policy == other.policy
240 }
241}
242
243impl<C> fmt::Debug for CharsetEncoder<C>
244where
245 C: CharsetEncodeProbe + fmt::Debug,
246{
247 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249 f.debug_struct("CharsetEncoder")
250 .field("engine", &self.engine)
251 .field("unmappable_action", &self.unmappable_action())
252 .field("replacement", &self.replacement())
253 .field("replacement_units_len", &self.replacement_units_len)
254 .finish()
255 }
256}