use encoding8;
use opts::{self, CFlag, Error::ConflictingOption};
use results::{Error, Result};
#[derive(Debug, Clone, Default)]
pub struct Converter {
pub encode_to: EncodeTo,
pub case: Case,
pub swap_bytes: bool,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Case {
None,
Lower,
Upper,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[allow(non_camel_case_types)]
pub enum EncodeTo {
None,
ASCII,
EBCDIC,
IBM,
OLD_ASCII,
OLD_EBCDIC,
OLD_IBM,
}
pub trait ConvertSlice<T> {
fn convert_slice(&self, buf: &mut [T]);
fn convert_clone(&self, buf: &[T]) -> Vec<T>
where
T: Clone + Default,
{
let mut dest: Vec<T> = vec![T::default(); buf.len()];
dest.clone_from_slice(buf);
self.convert_slice(&mut dest);
dest
}
fn convert_copy(&self, buf: &[T]) -> Vec<T>
where
T: Copy + Default,
{
let mut dest: Vec<T> = vec![T::default(); buf.len()];
dest.copy_from_slice(buf);
self.convert_slice(&mut dest);
dest
}
}
impl Converter {
pub fn new(o: &opts::Opts) -> Result<Self> {
Ok(Converter {
encode_to: EncodeTo::new(&o.cflags)?,
case: Case::new(&o.cflags)?,
swap_bytes: o.cflag(CFlag::SWAB),
})
}
}
impl ConvertSlice<u8> for Converter {
fn convert_slice(&self, buf: &mut [u8]) {
match self.encode_to {
EncodeTo::ASCII | EncodeTo::OLD_ASCII => {
self.encode_to.convert_slice(buf);
self.case.convert_slice(buf);
},
_ => {
self.case.convert_slice(buf);
self.encode_to.convert_slice(buf);
},
};
if self.swap_bytes {
swap_pairs(buf)
}
}
}
impl<F, T> ConvertSlice<T> for F
where
F: Fn(T) -> T,
T: Copy,
{
fn convert_slice(&self, slice: &mut [T]) { slice.iter_mut().for_each(|t| *t = (self)(*t)) }
}
impl Default for EncodeTo {
fn default() -> Self { EncodeTo::None }
}
impl Case {
pub fn new(c: &CFlag) -> Result<Self> {
match (c.contains(CFlag::LCASE), c.contains(CFlag::UCASE)) {
(true, false) => Ok(Case::Lower),
(false, true) => Ok(Case::Upper),
(false, false) => Ok(Case::None),
(true, true) => Err(Error::from(ConflictingOption(
"options 'lcase' and 'ucase' are mutually exclusives",
))),
}
}
}
impl ConvertSlice<u8> for Case {
fn convert_slice(&self, slice: &mut [u8]) {
match self {
Case::Lower => slice.iter_mut().for_each(u8::make_ascii_lowercase),
Case::Upper => slice.iter_mut().for_each(u8::make_ascii_uppercase),
Case::None => {},
}
}
}
pub fn swap_pairs<T>(slice: &mut [T]) { (0..(slice.len() / 2)).for_each(|i| slice.swap(2 * i, 2 * i + 1)) }
impl EncodeTo {
fn new(c: &CFlag) -> Result<Self> {
match (
c.contains(CFlag::ASCII),
c.contains(CFlag::EBCDIC),
c.contains(CFlag::IBM),
c.contains(CFlag::OLDASCII),
c.contains(CFlag::OLDIBM),
c.contains(CFlag::OLDEBCDIC),
) {
(true, false, false, false, false, false) => Ok(EncodeTo::ASCII),
(false, true, false, false, false, false) => Ok(EncodeTo::EBCDIC),
(false, false, true, false, false, false) => Ok(EncodeTo::IBM),
(false, false, false, true, false, false) => Ok(EncodeTo::OLD_ASCII),
(false, false, false, false, true, false) => Ok(EncodeTo::OLD_EBCDIC),
(false, false, false, false, false, true) => Ok(EncodeTo::OLD_IBM),
(false, false, false, false, false, false) => Ok(EncodeTo::None),
_ => Err(Error::from(ConflictingOption(concat!(
"must specify at most one of the following conversion flags: ",
"'ascii', 'ebcdic', 'ibm', 'old_ascii', 'old_ebcdic', 'old_imb'"
)))),
}
}
}
impl Default for Case {
fn default() -> Self { Case::None }
}
impl ConvertSlice<u8> for EncodeTo {
fn convert_slice(&self, s: &mut [u8]) {
match self {
EncodeTo::ASCII => s.iter_mut().for_each(encoding8::ebcdic::make_ascii),
EncodeTo::EBCDIC => s.iter_mut().for_each(encoding8::ascii::make_ebcdic),
EncodeTo::None => {},
encoding => unimplemented!("EncodeTo::{:?}.convert_slice", encoding),
}
}
}