use core::fmt;
use qubit_codec::{
BufferedEncodeEngine,
BufferedEncoder,
BufferedTranscoder,
CapacityError,
FinishError,
TranscodeProgress,
};
use crate::{
CharsetEncodeError,
UnmappableAction,
};
use super::{
charset_encode_hooks::{
CharsetEncodeHooks,
replacement_len,
},
charset_encode_policy::CharsetEncodePolicy,
charset_encode_probe::CharsetEncodeProbe,
};
#[derive(Clone)]
pub struct CharsetEncoder<C>
where
C: CharsetEncodeProbe,
{
engine: BufferedEncodeEngine<C, CharsetEncodeHooks<C::Unit>>,
policy: CharsetEncodePolicy,
replacement_units_len: usize,
}
impl<C> CharsetEncoder<C>
where
C: CharsetEncodeProbe,
{
#[must_use]
pub fn new(codec: C) -> Self {
let policy = CharsetEncodePolicy::default();
match Self::create_hooks(&codec, policy) {
Ok((hooks, replacement_units_len)) => Self {
engine: BufferedEncodeEngine::new(codec, hooks),
policy,
replacement_units_len,
},
Err(default_error) => {
let fallback_policy = CharsetEncodePolicy::replace(
CharsetEncodePolicy::DEFAULT_FALLBACK_REPLACEMENT,
);
match Self::create_hooks(&codec, fallback_policy) {
Ok((hooks, replacement_units_len)) => Self {
engine: BufferedEncodeEngine::new(codec, hooks),
policy: fallback_policy,
replacement_units_len,
},
Err(_) => panic!(
"cannot initialize CharsetEncoder for {:?}: neither {:?} nor {:?} is encodable ({default_error})",
codec.charset(),
CharsetEncodePolicy::DEFAULT_REPLACEMENT,
CharsetEncodePolicy::DEFAULT_FALLBACK_REPLACEMENT,
),
}
}
}
}
pub fn with_policy(
codec: C,
policy: CharsetEncodePolicy,
) -> Result<Self, CharsetEncodeError> {
let (hooks, replacement_units_len) =
Self::create_hooks(&codec, policy)?;
Ok(Self {
engine: BufferedEncodeEngine::new(codec, hooks),
policy,
replacement_units_len,
})
}
#[must_use]
#[inline(always)]
pub const fn unmappable_action(&self) -> UnmappableAction {
self.policy.unmappable_action()
}
#[must_use]
#[inline(always)]
pub const fn replacement(&self) -> char {
self.policy.replacement()
}
pub(crate) fn create_hooks(
codec: &C,
policy: CharsetEncodePolicy,
) -> Result<(CharsetEncodeHooks<C::Unit>, usize), CharsetEncodeError> {
let mut hooks = CharsetEncodeHooks::new(
policy.unmappable_action(),
policy.replacement(),
);
if policy.unmappable_action() != UnmappableAction::Replace {
return Ok((hooks, 0));
}
let replacement_units_len =
replacement_len(codec, policy.replacement())?;
hooks.set_replacement_units_len(replacement_units_len);
Ok((hooks, replacement_units_len))
}
}
impl<C> BufferedTranscoder<char, C::Unit> for CharsetEncoder<C>
where
C: CharsetEncodeProbe,
{
type Error = CharsetEncodeError;
#[inline(always)]
fn max_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
self.engine.max_output_len(input_len)
}
#[inline(always)]
fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
Ok(self.engine.max_finish_output_len())
}
#[inline(always)]
fn reset(&mut self) {
self.engine.reset();
}
#[inline(always)]
fn transcode(
&mut self,
input: &[char],
input_index: usize,
output: &mut [C::Unit],
output_index: usize,
) -> Result<TranscodeProgress, Self::Error> {
self.engine
.transcode(input, input_index, output, output_index)
}
#[inline(always)]
fn finish(
&mut self,
output: &mut [C::Unit],
output_index: usize,
) -> Result<usize, FinishError<Self::Error>> {
self.engine.finish(output, output_index)
}
}
impl<C> BufferedEncoder<char, C::Unit> for CharsetEncoder<C> where
C: CharsetEncodeProbe
{
}
impl<C> Eq for CharsetEncoder<C> where C: CharsetEncodeProbe + Eq {}
impl<C> PartialEq for CharsetEncoder<C>
where
C: CharsetEncodeProbe + PartialEq,
{
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
self.engine == other.engine && self.policy == other.policy
}
}
impl<C> fmt::Debug for CharsetEncoder<C>
where
C: CharsetEncodeProbe + fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CharsetEncoder")
.field("engine", &self.engine)
.field("unmappable_action", &self.unmappable_action())
.field("replacement", &self.replacement())
.field("replacement_units_len", &self.replacement_units_len)
.finish()
}
}