display_buffered 0.1.5

A small library that provides convinience functions to write all the elements into a writer with buffering
Documentation
//! Provides convinience functions to write all the elements into a writer with buffering

use std::{
    fmt::Display,
    io::{self, BufWriter, Write},
};

/// Writes all the items into the writer with buffering sepparating them with the new line
///
/// # Arguments
///
/// * values - iterator of values to print
/// * writer - buffer to write into
///
/// # Errors
///
/// Errors if the writing or flushing the buffer errors
///
/// # Example
///
/// ```
/// use display_buffered::display_buffered;
///
/// use std::io::stdout;
///
/// display_buffered([10, 20, 30], stdout()).unwrap()
/// ```
#[inline]
pub fn display_buffered<I, T, W>(values: I, writer: W) -> Result<(), io::Error>
where
    T: Display,
    I: IntoIterator<Item = T>,
    W: Write,
{
    let values = values.into_iter().map(|item| format!("{item}\n"));

    write_buffered(values, writer)
}

/// Writes all the bytes from values into the writer with buffering without any separators.
/// Worth noting that values may also consist of String and &str and any other objects that can
/// be turned into &[u8] using as_ref method
///
/// # Arguments
///
/// * values - iterator of elements to write
/// * writer - buffer to write into
///
/// # Errors
///
/// Errors if the writing or flushing the buffer errors
///
/// # Example
///
/// ```
/// use display_buffered::write_buffered;
///
/// use std::io::stdout;
///
/// write_buffered(["10", "20", "30"], stdout()).unwrap() // Prints 102030
/// ```
#[inline]
pub fn write_buffered<T, I, W>(values: I, writer: W) -> Result<(), io::Error>
where
    T: AsRef<[u8]>,
    I: IntoIterator<Item = T>,
    W: Write,
{
    let mut writer = BufWriter::new(writer);

    for bytes in values {
        writer.write_all(bytes.as_ref())?
    }

    writer.flush()
}

/// Writes all the elements from values into the writer with the provided separator.
/// The separator isn't written after the last element
///
/// # Arguments
///
/// * values - iterator of elements to write
/// * writer - buffer to write into
/// * sep - separator to use
///
/// # Errors
///
/// Errors if the writing or flushing the buffer errors
///
/// # Example
///
/// ```
/// use display_buffered::write_buffered_separated;
///
/// use std::io::stdout;
///
/// // Prints "It_Just_Works"
/// write_buffered_separated(["It", "Just", "Works"], stdout(), b"_").unwrap()
/// ```
#[inline]
pub fn write_buffered_separated<T, I, W>(
    values: I,
    mut writer: W,
    sep: &[u8],
) -> Result<(), io::Error>
where
    T: AsRef<[u8]>,
    I: IntoIterator<Item = T>,
    W: Write,
{
    let mut values = values.into_iter();
    // We are not initializing buf_writer here to avoid unnessesary heap allocation if there're no values
    let mut buf_writer;
    match values.next() {
        Some(val) => {
            buf_writer = BufWriter::new(writer);
            buf_writer.write_all(val.as_ref())?
        }
        None => return writer.flush(),
    }

    for bytes in values {
        buf_writer.write_all(sep)?;
        buf_writer.write_all(bytes.as_ref())?
    }

    buf_writer.flush()
}

/// Writes all the elements from values into the writer with the computed separator.
/// The separator isn't written after the last element
///
/// # Arguments
///
/// * values - iterator of elements to write
/// * writer - buffer to write into
/// * f - Function that computes the sepparator from the index and the value of next printed value
///
/// # Errors
///
/// Errors if the writing or flushing the buffer errors
///
/// # Example
///
/// ```
/// use display_buffered::write_buffered_separated_with;
///
/// use std::io::stdout;
///
/// write_buffered_separated_with(["It", "Just", "Works"], stdout(), |i, _| {
/// if i % 2 == 0 {
///     "_"
/// } else {
///     "-"
/// }
/// })
/// .unwrap(); // Prints "It-Just_Works"
/// ```
#[inline]
pub fn write_buffered_separated_with<T, I, W, U, F>(
    values: I,
    mut writer: W,
    mut f: F,
) -> Result<(), io::Error>
where
    T: AsRef<[u8]>,
    I: IntoIterator<Item = T>,
    W: Write,
    U: AsRef<[u8]>,
    F: FnMut(usize, &[u8]) -> U,
{
    let mut values = values.into_iter().enumerate();
    // We are not initializing buf_writer here to avoid unnessesary heap allocation if there're no values
    let mut buf_writer;
    match values.next() {
        Some((_, val)) => {
            buf_writer = BufWriter::new(writer);
            buf_writer.write_all(val.as_ref())?
        }
        None => return writer.flush(),
    }

    for (pos, bytes) in values {
        let bytes = bytes.as_ref();
        buf_writer.write_all(f(pos, bytes).as_ref())?;
        buf_writer.write_all(bytes)?
    }

    buf_writer.flush()
}

#[cfg(test)]
mod tests {
    use super::*;

    /// A Write implementation for testing
    struct Writer(Vec<u8>);

    impl Writer {
        fn new() -> Self {
            Self(Vec::new())
        }

        fn into_inner(self) -> Vec<u8> {
            self.0
        }
    }

    impl Write for Writer {
        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
            self.0.extend(buf);
            Ok(buf.len())
        }

        fn flush(&mut self) -> io::Result<()> {
            Ok(())
        }
    }

    #[test]
    fn test_display_buffered() {
        let mut writer = Writer::new();
        display_buffered([10, 20], &mut writer).unwrap();
        assert_eq!(writer.into_inner(), b"10\n20\n")
    }

    #[test]
    fn test_write_buffered_with_slices() {
        let mut writer = Writer::new();
        write_buffered(["It ", "Works"], &mut writer).unwrap();
        assert_eq!(writer.into_inner(), b"It Works")
    }

    #[test]
    fn test_write_buffered_with_strings() {
        let mut writer = Writer::new();
        write_buffered(["It ".to_owned(), "Works".to_owned()], &mut writer).unwrap();
        assert_eq!(writer.into_inner(), b"It Works")
    }

    #[test]
    fn test_write_buffered_with_bytes() {
        let mut writer = Writer::new();
        let values: [&[u8]; 2] = [b"It ", b"Works"];
        write_buffered(values, &mut writer).unwrap();
        assert_eq!(writer.into_inner(), b"It Works")
    }

    #[test]
    fn test_write_buffered_with_separator() {
        let mut writer = Writer::new();
        write_buffered_separated(["It", "Just", "Works"], &mut writer, b" ").unwrap();
        assert_eq!(writer.into_inner(), b"It Just Works")
    }

    #[test]
    fn test_write_buffered_separated_with() {
        let mut writer = Writer::new();
        write_buffered_separated_with(["It", "Just", "Works"], &mut writer, |i, _| {
            if i % 2 == 0 {
                "_"
            } else {
                "-"
            }
        })
        .unwrap();
        assert_eq!(
            String::from_utf8(writer.into_inner()).unwrap(),
            "It-Just_Works"
        )
    }
}