use crate::error::{Result, TextEncodingError};
use encoding::all::{ISO_8859_1, UTF_8};
use encoding::{DecoderTrap, EncoderTrap, Encoding, RawDecoder, StringWriter};
use std::fmt::Debug;
pub trait TextCodec: Debug {
fn decode(&self, text: &[u8]) -> Result<String>;
fn encode(&self, text: &str) -> Result<Vec<u8>>;
}
impl<T: ?Sized> TextCodec for Box<T>
where
T: TextCodec,
{
fn decode(&self, text: &[u8]) -> Result<String> {
self.as_ref().decode(text)
}
fn encode(&self, text: &str) -> Result<Vec<u8>> {
self.as_ref().encode(text)
}
}
impl<'a, T: ?Sized> TextCodec for &'a T
where
T: TextCodec,
{
fn decode(&self, text: &[u8]) -> Result<String> {
(*self).decode(text)
}
fn encode(&self, text: &str) -> Result<Vec<u8>> {
(*self).encode(text)
}
}
pub type DynamicTextCodec = Box<dyn TextCodec>;
#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
pub enum SpecificCharacterSet {
Default,
IsoIr192,
}
impl Default for SpecificCharacterSet {
fn default() -> Self {
SpecificCharacterSet::Default
}
}
impl SpecificCharacterSet {
pub fn from_code(uid: &str) -> Option<Self> {
use self::SpecificCharacterSet::*;
match uid {
"Default" | "ISO_IR_6" => Some(Default),
"ISO_IR_192" => Some(IsoIr192),
_ => None,
}
}
pub fn get_codec(self) -> Option<Box<dyn TextCodec>> {
match self {
SpecificCharacterSet::Default => Some(Box::new(DefaultCharacterSetCodec)),
SpecificCharacterSet::IsoIr192 => Some(Box::new(Utf8CharacterSetCodec)),
}
}
}
fn decode_text_trap(
_decoder: &mut dyn RawDecoder,
input: &[u8],
output: &mut dyn StringWriter,
) -> bool {
let c = input[0];
let o0 = c & 7;
let o1 = (c & 56) >> 3;
let o2 = (c & 192) >> 6;
output.write_char('\\');
output.write_char((o2 + b'0') as char);
output.write_char((o1 + b'0') as char);
output.write_char((o0 + b'0') as char);
true
}
#[derive(Debug, Default, Clone, PartialEq, Eq, Copy)]
pub struct DefaultCharacterSetCodec;
impl TextCodec for DefaultCharacterSetCodec {
fn decode(&self, text: &[u8]) -> Result<String> {
ISO_8859_1
.decode(text, DecoderTrap::Call(decode_text_trap))
.map_err(|e| TextEncodingError::new(e).into())
}
fn encode(&self, text: &str) -> Result<Vec<u8>> {
ISO_8859_1
.encode(text, EncoderTrap::Strict)
.map_err(|e| TextEncodingError::new(e).into())
}
}
#[derive(Debug, Default, Clone, PartialEq, Eq, Copy)]
pub struct Utf8CharacterSetCodec;
impl TextCodec for Utf8CharacterSetCodec {
fn decode(&self, text: &[u8]) -> Result<String> {
UTF_8
.decode(text, DecoderTrap::Call(decode_text_trap))
.map_err(|e| TextEncodingError::new(e).into())
}
fn encode(&self, text: &str) -> Result<Vec<u8>> {
UTF_8
.encode(text, EncoderTrap::Strict)
.map_err(|e| TextEncodingError::new(e).into())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum TextValidationOutcome {
Ok,
BadCharacters,
NotOk,
}
pub fn validate_iso_8859(text: &[u8]) -> TextValidationOutcome {
if ISO_8859_1.decode(text, DecoderTrap::Strict).is_err() {
match ISO_8859_1.decode(text, DecoderTrap::Call(decode_text_trap)) {
Ok(_) => TextValidationOutcome::BadCharacters,
Err(_) => TextValidationOutcome::NotOk,
}
} else {
TextValidationOutcome::Ok
}
}
pub fn validate_da(text: &[u8]) -> TextValidationOutcome {
if text.iter().cloned().all(|c| c >= b'0' && c <= b'9') {
TextValidationOutcome::Ok
} else {
TextValidationOutcome::NotOk
}
}
pub fn validate_tm(text: &[u8]) -> TextValidationOutcome {
if text.iter().cloned().all(|c| match c {
b'\\' | b'.' | b'-' | b' ' => true,
c => c >= b'0' && c <= b'9',
}) {
TextValidationOutcome::Ok
} else {
TextValidationOutcome::NotOk
}
}
pub fn validate_dt(text: &[u8]) -> TextValidationOutcome {
if text.iter().cloned().all(|c| match c {
b'.' | b'-' | b'+' | b' ' | b'\\' => true,
c => c >= b'0' && c <= b'9',
}) {
TextValidationOutcome::Ok
} else {
TextValidationOutcome::NotOk
}
}
pub fn validate_cs(text: &[u8]) -> TextValidationOutcome {
if text.iter().cloned().all(|c| match c {
b' ' | b'_' => true,
c => (c >= b'0' && c <= b'9') || (c >= b'A' && c <= b'Z'),
}) {
TextValidationOutcome::Ok
} else {
TextValidationOutcome::NotOk
}
}