dd-lib 0.2.1

library functions for a clone of the unix coreutil dd
Documentation
use super::{ConvertSlice, ErrHandler, Reader};

#[allow(unused_imports)] // for documentatino
use opts;
use results::{Result, Success};
use std::{
    cmp::Ordering,
    io::{BufRead, Read, Write},
    time::SystemTime,
};

/// Copy bytes from R to W, converting according to C. See
/// [opts::Mode::Standard]
pub fn standard<R, W, C, E>(mut r: Reader<R>, w: &mut W, c: C, mut e: ErrHandler<E>) -> Result<Success>
where
    R: Read,
    W: Write,
    C: ConvertSlice<u8>,
    E: Write,
{
    let start = SystemTime::now();
    let mut bytes = 0;
    loop {
        let converted = match e.handle(r.fill_buf())? {
            Some(ref buf) if buf.len() == 0 => return Ok(Success::Bytes { bytes, start }),
            Some(buf) => c.convert_copy(buf),
            None => continue,
        };

        e.handle(w.write_all(&converted))?;
        r.consume(converted.len());
        bytes += converted.len();
        e.report_status_standard(bytes, &start)?
    }
}
pub fn bytes<R, W, C, E>(mut r: R, w: &mut W, c: &C, mut e: ErrHandler<E>, max_bytes: usize) -> Result<Success>
where
    R: BufRead,
    W: Write,
    C: ConvertSlice<u8>,
    E: Write,
{
    let start = SystemTime::now();
    let mut bytes = 0;
    loop {
        let converted = match e.handle(r.fill_buf())? {
            None => continue,
            Some(buf) => match usize::min(max_bytes.saturating_sub(bytes), buf.len()) {
                0 => return Ok(Success::Bytes { bytes, start }),
                n => c.convert_copy(&buf[..n]),
            },
        };

        e.handle(w.write_all(&converted))?;
        r.consume(converted.len());
        bytes += converted.len();
    }
}

/// Copy a series of newline or EOF-terminated blocks from R to W, converting
/// according to C. [opts::Mode::Unblock] treats the input as a sequence of '\n'
/// or EOF-terminated variable length records independent of input and output
/// block boundaries.  Any trailing '\n' is discarded. Each input record is
/// converted to a fixed length output record where the length is specified by
/// the [conversion_block_size][opts] operand.  Input records shorter than the
/// conversion record size are padded with spaces. Input records longer than the
/// conversion record size are truncated.  The number of truncated input
/// records, if any, are reported to the standard error output at the completion
/// of the copy.
pub fn block<R, W, C, E>(r: R, w: &mut W, c: C, mut e: ErrHandler<E>, block_size: usize) -> Result<Success>
where
    R: BufRead,
    W: Write,
    C: ConvertSlice<u8>,
    E: Write,
{
    let (mut lines, mut padded, mut truncated): (usize, usize, usize) = (0, 0, 0);
    let (mut buf, mut bytes): (Vec<u8>, std::io::Bytes<_>) = (Vec::with_capacity(block_size), r.bytes());
    let start = SystemTime::now();
    while {
        if let Some(b) = skip_trailing_while_match(&mut bytes, &b'\n')? {
            buf.push(b)
        };
        e.handle(fill_buf_until_match(&mut bytes, &mut buf, b'\n'))?;
        buf.len() != 0
    } {
        lines += 1;
        match buf.len().cmp(&block_size) {
            Ordering::Greater => truncated += 1,
            Ordering::Less => padded += 1,
            Ordering::Equal => {},
        }

        c.convert_slice(&mut buf);
        buf.resize(block_size, b' ');
        w.write_all(&buf)?;
        e.report_status_block(lines, &start);
        buf.clear();
    }
    Ok(Success::Block {
        start,
        lines,
        truncated,
        padded,
        block_size,
    })
}

/// Copy a series of fixed-length records from R to W, converting according to
/// C. [opts::Mode::Unblock] treats the input as a sequence of fixed length
/// records independent of input and output block boundaries.  The length of the
/// input records is specified by the [conversion_block_size][opts] option
/// option. Any trailing space characters are discarded and a newline character
/// is appended.
pub fn unblock<R, W, C, E>(r: R, w: &mut W, c: C, mut e: ErrHandler<E>, block_size: usize) -> Result<Success>
where
    R: BufRead,
    W: Write,
    C: ConvertSlice<u8>,
    E: Write,
{
    let start = SystemTime::now();
    let mut buf: Vec<u8> = Vec::with_capacity(block_size + 1); // account for newline
    let mut bytes = r.bytes();
    let mut blocks = 0;
    while {
        if let Some(Some(b)) = e.handle(skip_trailing_while_match(&mut bytes, &b' '))? {
            buf.push(b)
        };
        fill_buf_to(&mut bytes, &mut buf, block_size)?;
        buf.len() != 0
    } {
        buf.push(b'\n');

        c.convert_slice(&mut buf);
        e.handle(w.write_all(&mut buf))?;

        blocks += 1;
        e.report_status_unblock(blocks, &start);
        buf.clear();
    }
    Ok(Success::Unblock {
        blocks,
        block_size,
        start,
    })
}

/// skip items while they  match `unwanted_byte`, returning the first byte
/// (if any) that doesn't match

fn skip_trailing_while_match<I, T>(it: &mut I, unwanted: &T) -> std::io::Result<Option<T>>
where
    I: Iterator<Item = ::std::io::Result<T>>,
    T: PartialEq,
{
    loop {
        match it.next() {
            Some(Ok(ref t)) if t == unwanted => {},
            Some(t) => return Ok(Some(t?)),
            None => return Ok(None),
        }
    }
}

/// fill the buffer from `it` until and including the first item that matches
/// `want`
/// ```
/// # use dd_lib::io::copy::fill_buf_until_match;
/// let mut it = vec![1, 1, 2].into_iter().map(|t| Ok(t));
/// let mut buf: Vec<u8> = vec![3];
/// fill_buf_until_match(&mut it, &mut buf, 3).unwrap();
/// assert_eq!(vec![3, 1, 1, 2], buf);
/// ```
pub fn fill_buf_until_match<I, T>(it: &mut I, buf: &mut Vec<T>, want: T) -> std::io::Result<()>
where
    I: Iterator<Item = std::io::Result<T>>,
    T: PartialEq,
{
    loop {
        match it.next() {
            Some(Ok(ref t)) if t == &want => return Ok(()),
            Some(Ok(t)) => buf.push(t),
            Some(Err(err)) => return Err(err),
            None => return Ok(()),
        }
    }
}

/// fill the buffer with unwrapped elements from `it` until it has len `cap`
/// ```
/// # use dd_lib::io::copy::fill_buf_to;
/// let mut fours = vec![4; 80].into_iter().map(|t| Ok(t));
/// let mut buf = vec![1_u8];
/// fill_buf_to(&mut fours, &mut buf, 5).unwrap();
///
/// assert_eq!(vec![1, 4, 4, 4, 4], buf);
/// let remainder: Vec<u8> = fours.map(Result::unwrap).collect();
/// assert_eq!(vec![4; 80 - 4], remainder)
/// ```
pub fn fill_buf_to<I, T>(it: &mut I, buf: &mut Vec<T>, cap: usize) -> std::io::Result<()>
where
    I: Iterator<Item = std::io::Result<T>>,
{
    for _ in buf.len()..cap {
        match it.next() {
            Some(Ok(t)) => buf.push(t),
            Some(Err(err)) => return Err(err),
            None => break,
        };
    }
    Ok(())
}