use super::{
charset_codec::CharsetCodec,
charset_convert_error::CharsetConvertError,
charset_convert_hooks::CharsetConvertHooks,
};
use crate::{
CharsetDecodePolicy,
CharsetEncodeError,
CharsetEncodeErrorKind,
CharsetEncodeHooks,
CharsetEncodePolicy,
CharsetEncodeProbe,
CharsetEncoder,
MalformedAction,
UnmappableAction,
};
use qubit_codec::{
BufferedConvertEngine,
BufferedConverter,
BufferedTranscoder,
CapacityError,
FinishError,
TranscodeProgress,
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CharsetConverter<D, E>
where
D: CharsetCodec,
E: CharsetEncodeProbe,
{
engine: BufferedConvertEngine<D, E, CharsetConvertHooks<E::Unit>>,
decode_policy: CharsetDecodePolicy,
encode_policy: CharsetEncodePolicy,
}
impl<D, E> CharsetConverter<D, E>
where
D: CharsetCodec,
E: CharsetEncodeProbe,
{
#[must_use]
pub fn from_codecs(source: D, target: E) -> Self {
let decode_policy = CharsetDecodePolicy::default();
let (encode_policy, encode_hooks) =
Self::default_encode_policy(&target);
Self {
engine: BufferedConvertEngine::new(
source,
target,
CharsetConvertHooks::with_policies(
decode_policy,
encode_policy,
encode_hooks,
),
),
decode_policy,
encode_policy,
}
}
pub fn from_codecs_with_policies(
source: D,
target: E,
decode_policy: CharsetDecodePolicy,
encode_policy: CharsetEncodePolicy,
) -> Result<Self, CharsetEncodeError> {
let (encode_hooks, _) =
CharsetEncoder::<E>::create_hooks(&target, encode_policy)?;
Ok(Self {
engine: BufferedConvertEngine::new(
source,
target,
CharsetConvertHooks::with_policies(
decode_policy,
encode_policy,
encode_hooks,
),
),
decode_policy,
encode_policy,
})
}
#[must_use]
#[inline(always)]
pub const fn decode_policy(&self) -> CharsetDecodePolicy {
self.decode_policy
}
#[must_use]
#[inline(always)]
pub const fn encode_policy(&self) -> CharsetEncodePolicy {
self.encode_policy
}
#[must_use]
#[inline(always)]
pub const fn malformed_action(&self) -> MalformedAction {
self.decode_policy.malformed_action()
}
#[must_use]
#[inline(always)]
pub const fn decode_replacement(&self) -> char {
self.decode_policy.replacement()
}
#[must_use]
#[inline(always)]
pub const fn unmappable_action(&self) -> UnmappableAction {
self.encode_policy.unmappable_action()
}
#[must_use]
#[inline(always)]
pub const fn replacement(&self) -> char {
self.encode_policy.replacement()
}
fn default_encode_policy(
target: &E,
) -> (CharsetEncodePolicy, CharsetEncodeHooks<E::Unit>) {
let default_policy = CharsetEncodePolicy::default();
match CharsetEncoder::<E>::create_hooks(target, default_policy) {
Ok((hooks, _)) => (default_policy, hooks),
Err(_) => {
let fallback_policy = CharsetEncodePolicy::replace(
CharsetEncodePolicy::DEFAULT_FALLBACK_REPLACEMENT,
);
if let Ok((hooks, _)) =
CharsetEncoder::<E>::create_hooks(target, fallback_policy)
{
return (fallback_policy, hooks);
}
let kind = CharsetEncodeErrorKind::UnmappableCharacter {
value: CharsetEncodePolicy::DEFAULT_FALLBACK_REPLACEMENT
as u32,
};
panic!(
"cannot initialize CharsetConverter target for {:?}: neither {:?} nor {:?} is encodable ({})",
target.charset(),
CharsetEncodePolicy::DEFAULT_REPLACEMENT,
CharsetEncodePolicy::DEFAULT_FALLBACK_REPLACEMENT,
CharsetEncodeError::new(target.charset(), kind, 0),
);
}
}
}
}
impl<D, E> BufferedTranscoder<D::Unit, E::Unit> for CharsetConverter<D, E>
where
D: CharsetCodec,
E: CharsetEncodeProbe,
{
type Error = CharsetConvertError;
#[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> {
self.engine.max_finish_output_len()
}
#[inline(always)]
fn reset(&mut self) {
self.engine.reset();
}
#[inline(always)]
fn transcode(
&mut self,
input: &[D::Unit],
input_index: usize,
output: &mut [E::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 [E::Unit],
output_index: usize,
) -> Result<usize, FinishError<Self::Error>> {
self.engine.finish(output, output_index)
}
}
impl<D, E> BufferedConverter<D::Unit, E::Unit> for CharsetConverter<D, E>
where
D: CharsetCodec,
E: CharsetEncodeProbe,
{
}