dd_lib/io/
conv.rs

1use encoding8;
2use opts::{self, CFlag, Error::ConflictingOption};
3use results::{Error, Result};
4#[derive(Debug, Clone, Default)]
5/// A converter modifies the blocks read from a [`Reader`][super::read::Reader]
6/// before they are written to a [`Writer`][super::write::Writer]
7pub struct Converter {
8    /// The text encoding to convert to, if any
9    pub encode_to: EncodeTo,
10    /// The case to convert text to, if any
11    pub case: Case,
12    /// Whether or not to swap pairs of bytes
13    pub swap_bytes: bool,
14}
15
16#[derive(Copy, Clone, Debug, PartialEq, Eq)]
17/// The case to convert to. The effect of this is dependent on [`EncodeTo`]; the
18/// conversion will happen AFTER the encoding in the case of
19/// `ASCII`, `OLDASCII`, and`None`. `EBCDIC`, `IBM`, and `OLD_IBM`
20/// mean the case conversion will happen BEFORE encoding.
21pub enum Case {
22    None,
23    Lower,
24    Upper,
25}
26
27#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
28#[allow(non_camel_case_types)]
29/// The Encoding to convert to. `ASCII` and `OLD_ASCII`
30/// assume that the source file is encoded in classic `EBCDIC`,
31/// while `IBM`, `EBCDIC`, `OLD_IBM`, and `OLD_EBCDIC` assume that the source
32/// file is encoded in modern (that is, post-1967) ASCII
33pub enum EncodeTo {
34    None,
35    /// Convert **to** ASCII from EBCDIC
36    ASCII,
37    /// Convert **to** EBCDIC from ASCII
38    EBCDIC,
39    /// <UNIMPLEMENTD> Convert **to** IBM from ASCII
40    IBM,
41    /// <UNIMPLEMENTED> Convert **to** 1963 ASCII from EBCDIC
42    OLD_ASCII,
43    /// <UNIMPLEMENTED> Convert **to** old ebcdic from ASCII
44    OLD_EBCDIC,
45    /// <UNIMPLEMENTED> Convert **to** OLD_IBM from ASCII
46    OLD_IBM,
47}
48/// A convertslice can modify a slice of `T` in-place, and provides helper
49/// methods to create modified copies.
50pub trait ConvertSlice<T> {
51    /// modify a slice of `T` in-place.
52    fn convert_slice(&self, buf: &mut [T]);
53
54    /// modify a copy of `T` created using `clone()`. This is less efficient
55    /// than [`convert_copy`][ConvertSlice::convert_copy].
56    fn convert_clone(&self, buf: &[T]) -> Vec<T>
57    where
58        T: Clone + Default,
59    {
60        let mut dest: Vec<T> = vec![T::default(); buf.len()];
61
62        dest.clone_from_slice(buf);
63        self.convert_slice(&mut dest);
64        dest
65    }
66    /// modify a copy of `T`. This is more efficient than
67    /// [`convert_clone`][ConvertSlice::convert_clone] but has tighter
68    /// bounds.
69
70    fn convert_copy(&self, buf: &[T]) -> Vec<T>
71    where
72        T: Copy + Default,
73    {
74        let mut dest: Vec<T> = vec![T::default(); buf.len()];
75
76        dest.copy_from_slice(buf);
77        self.convert_slice(&mut dest);
78        dest
79    }
80}
81
82impl Converter {
83    pub fn new(o: &opts::Opts) -> Result<Self> {
84        Ok(Converter {
85            encode_to: EncodeTo::new(&o.cflags)?,
86            case: Case::new(&o.cflags)?,
87            swap_bytes: o.cflag(CFlag::SWAB),
88        })
89    }
90}
91
92impl ConvertSlice<u8> for Converter {
93    fn convert_slice(&self, buf: &mut [u8]) {
94        match self.encode_to {
95            EncodeTo::ASCII | EncodeTo::OLD_ASCII => {
96                // turn to ASCII before switching case
97                self.encode_to.convert_slice(buf);
98                self.case.convert_slice(buf);
99            },
100            _ => {
101                // assumed to already be ASCII, so switch case first
102                self.case.convert_slice(buf);
103                self.encode_to.convert_slice(buf);
104            },
105        };
106        if self.swap_bytes {
107            swap_pairs(buf)
108        }
109    }
110}
111impl<F, T> ConvertSlice<T> for F
112where
113    F: Fn(T) -> T,
114    T: Copy,
115{
116    fn convert_slice(&self, slice: &mut [T]) { slice.iter_mut().for_each(|t| *t = (self)(*t)) }
117}
118
119impl Default for EncodeTo {
120    fn default() -> Self { EncodeTo::None }
121}
122
123impl Case {
124    pub fn new(c: &CFlag) -> Result<Self> {
125        match (c.contains(CFlag::LCASE), c.contains(CFlag::UCASE)) {
126            (true, false) => Ok(Case::Lower),
127            (false, true) => Ok(Case::Upper),
128            (false, false) => Ok(Case::None),
129            (true, true) => Err(Error::from(ConflictingOption(
130                "options 'lcase' and  'ucase' are mutually exclusives",
131            ))),
132        }
133    }
134}
135
136impl ConvertSlice<u8> for Case {
137    fn convert_slice(&self, slice: &mut [u8]) {
138        match self {
139            Case::Lower => slice.iter_mut().for_each(u8::make_ascii_lowercase),
140            Case::Upper => slice.iter_mut().for_each(u8::make_ascii_uppercase),
141            Case::None => {},
142        }
143    }
144}
145
146/// Swap every pair of items.  If `slice.len() % 2 == 1`, the last item will be
147/// ignored.
148pub fn swap_pairs<T>(slice: &mut [T]) { (0..(slice.len() / 2)).for_each(|i| slice.swap(2 * i, 2 * i + 1)) }
149
150impl EncodeTo {
151    fn new(c: &CFlag) -> Result<Self> {
152        match (
153            c.contains(CFlag::ASCII),
154            c.contains(CFlag::EBCDIC),
155            c.contains(CFlag::IBM),
156            c.contains(CFlag::OLDASCII),
157            c.contains(CFlag::OLDIBM),
158            c.contains(CFlag::OLDEBCDIC),
159        ) {
160            (true, false, false, false, false, false) => Ok(EncodeTo::ASCII),
161            (false, true, false, false, false, false) => Ok(EncodeTo::EBCDIC),
162            (false, false, true, false, false, false) => Ok(EncodeTo::IBM),
163            (false, false, false, true, false, false) => Ok(EncodeTo::OLD_ASCII),
164            (false, false, false, false, true, false) => Ok(EncodeTo::OLD_EBCDIC),
165            (false, false, false, false, false, true) => Ok(EncodeTo::OLD_IBM),
166            (false, false, false, false, false, false) => Ok(EncodeTo::None),
167            _ => Err(Error::from(ConflictingOption(concat!(
168                "must specify at most one of the following conversion flags: ",
169                "'ascii', 'ebcdic', 'ibm', 'old_ascii', 'old_ebcdic', 'old_imb'"
170            )))),
171        }
172    }
173}
174
175impl Default for Case {
176    fn default() -> Self { Case::None }
177}
178
179impl ConvertSlice<u8> for EncodeTo {
180    fn convert_slice(&self, s: &mut [u8]) {
181        match self {
182            EncodeTo::ASCII => s.iter_mut().for_each(encoding8::ebcdic::make_ascii),
183            EncodeTo::EBCDIC => s.iter_mut().for_each(encoding8::ascii::make_ebcdic),
184            EncodeTo::None => {},
185            encoding => unimplemented!("EncodeTo::{:?}.convert_slice", encoding),
186        }
187    }
188}