dd-lib 0.2.1

library functions for a clone of the unix coreutil dd
Documentation
use encoding8;
use opts::{self, CFlag, Error::ConflictingOption};
use results::{Error, Result};
#[derive(Debug, Clone, Default)]
/// A converter modifies the blocks read from a [`Reader`][super::read::Reader]
/// before they are written to a [`Writer`][super::write::Writer]
pub struct Converter {
    /// The text encoding to convert to, if any
    pub encode_to: EncodeTo,
    /// The case to convert text to, if any
    pub case: Case,
    /// Whether or not to swap pairs of bytes
    pub swap_bytes: bool,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
/// The case to convert to. The effect of this is dependent on [`EncodeTo`]; the
/// conversion will happen AFTER the encoding in the case of
/// `ASCII`, `OLDASCII`, and`None`. `EBCDIC`, `IBM`, and `OLD_IBM`
/// mean the case conversion will happen BEFORE encoding.
pub enum Case {
    None,
    Lower,
    Upper,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[allow(non_camel_case_types)]
/// The Encoding to convert to. `ASCII` and `OLD_ASCII`
/// assume that the source file is encoded in classic `EBCDIC`,
/// while `IBM`, `EBCDIC`, `OLD_IBM`, and `OLD_EBCDIC` assume that the source
/// file is encoded in modern (that is, post-1967) ASCII
pub enum EncodeTo {
    None,
    /// Convert **to** ASCII from EBCDIC
    ASCII,
    /// Convert **to** EBCDIC from ASCII
    EBCDIC,
    /// <UNIMPLEMENTD> Convert **to** IBM from ASCII
    IBM,
    /// <UNIMPLEMENTED> Convert **to** 1963 ASCII from EBCDIC
    OLD_ASCII,
    /// <UNIMPLEMENTED> Convert **to** old ebcdic from ASCII
    OLD_EBCDIC,
    /// <UNIMPLEMENTED> Convert **to** OLD_IBM from ASCII
    OLD_IBM,
}
/// A convertslice can modify a slice of `T` in-place, and provides helper
/// methods to create modified copies.
pub trait ConvertSlice<T> {
    /// modify a slice of `T` in-place.
    fn convert_slice(&self, buf: &mut [T]);

    /// modify a copy of `T` created using `clone()`. This is less efficient
    /// than [`convert_copy`][ConvertSlice::convert_copy].
    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
    }
    /// modify a copy of `T`. This is more efficient than
    /// [`convert_clone`][ConvertSlice::convert_clone] but has tighter
    /// bounds.

    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 => {
                // turn to ASCII before switching case
                self.encode_to.convert_slice(buf);
                self.case.convert_slice(buf);
            },
            _ => {
                // assumed to already be ASCII, so switch case first
                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 => {},
        }
    }
}

/// Swap every pair of items.  If `slice.len() % 2 == 1`, the last item will be
/// ignored.
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),
        }
    }
}