char-buf 0.1.2

A writable, fixed-length `char` buffer usable in `no_std` environments.
Documentation
#![no_std]

//! A writable, fixed-length `char` buffer usable in `no_std` environments.
//!
//! ```
//! use char_buf::CharBuf;
//! use core::fmt::Write;
//!
//! // `CharBuf` with capacity `8`
//! type CharBuf8 = CharBuf<8>;
//!
//! let mut w = CharBuf8::new();
//! write!(w, "x{:?}x", [1, 2]).unwrap();
//!
//! assert_eq!(w, "x[1, 2]x");
//! ```

use core::fmt::{self, Write};

/// A writable, fixed-length `char` buffer.
pub struct CharBuf<const N: usize> {
    buf: [char; N],
    len: usize,
}

impl<const N: usize> CharBuf<N> {
    /// Constructs a new, empty `CharBuf<N>`.
    pub const fn new() -> Self {
        CharBuf {
            buf: ['\0'; N],
            len: 0,
        }
    }
}

impl<const N: usize> Default for CharBuf<N> {
    fn default() -> Self {
        CharBuf::new()
    }
}

impl<const N: usize> fmt::Debug for CharBuf<N> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_char('"')?;
        for c in self.buf.split_at(self.len).0.iter() {
            f.write_char(*c)?;
        }
        f.write_char('"')
    }
}

impl<const N: usize> fmt::Write for CharBuf<N> {
    /// Writes a string slice into this writer, returning whether the write succeeded. [Read more]
    ///
    /// # Errors
    ///
    /// This function will return an instance of [`Error`] if the buffer length is not enough to write `s`.
    ///
    /// [Read more]: https://doc.rust-lang.org/core/fmt/trait.Write.html#tymethod.write_str
    /// [`Error`]: https://doc.rust-lang.org/core/fmt/struct.Error.html
    fn write_str(&mut self, s: &str) -> fmt::Result {
        let mut chars = s.chars();
        self.buf
            .split_at_mut(self.len)
            .1
            .iter_mut()
            .zip(chars.by_ref())
            .for_each(|(x, c)| {
                *x = c;
                self.len += 1;
            });
        if chars.next().is_some() {
            Err(fmt::Error)
        } else {
            Ok(())
        }
    }
}

impl<const N1: usize, const N2: usize> PartialEq<CharBuf<N2>> for CharBuf<N1> {
    fn eq(&self, other: &CharBuf<N2>) -> bool {
        self.len == other.len
            && self
                .buf
                .split_at(self.len)
                .0
                .iter()
                .zip(other.buf.split_at(other.len).0.iter())
                .all(|(x, y)| x == y)
    }
}

impl<const N: usize> Eq for CharBuf<N> {}

impl<const N: usize> PartialEq<str> for CharBuf<N> {
    fn eq(&self, other: &str) -> bool {
        let mut buf = self.buf.split_at(self.len).0.iter();
        let mut chars = other.chars();
        buf.by_ref().all(|x| {
            if let Some(c) = chars.next() {
                *x == c
            } else {
                false
            }
        }) && chars.next().is_none()
    }
}

impl<const N: usize> PartialEq<&str> for CharBuf<N> {
    fn eq(&self, other: &&str) -> bool {
        *self == **other
    }
}

impl<const N: usize> PartialEq<CharBuf<N>> for str {
    fn eq(&self, other: &CharBuf<N>) -> bool {
        *other == *self
    }
}

impl<const N: usize> PartialEq<CharBuf<N>> for &str {
    fn eq(&self, other: &CharBuf<N>) -> bool {
        *other == **self
    }
}

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

    const N1: usize = 3;
    const N2: usize = 5;
    type CharBufN1 = CharBuf<N1>;
    type CharBufN2 = CharBuf<N2>;

    #[test]
    fn new() {
        let w1 = CharBufN1::new();
        let w2 = CharBufN2::new();

        assert_eq!(w1.buf, ['\0'; N1]);
        assert_eq!(w1.len, 0);

        assert_eq!(w2.buf, ['\0'; N2]);
        assert_eq!(w2.len, 0);
    }

    #[test]
    fn default() {
        let w1 = CharBufN1::default();
        let w2 = CharBufN2::default();

        assert_eq!(w1.buf, ['\0'; N1]);
        assert_eq!(w1.len, 0);

        assert_eq!(w2.buf, ['\0'; N2]);
        assert_eq!(w2.len, 0);
    }

    #[test]
    fn debug() {
        let mut w1a = CharBufN1::new();
        let mut w1b = CharBufN1::new();

        let mut w2a = CharBufN2::new();
        let mut w2b = CharBufN2::new();

        write!(w1a, "a").unwrap();
        write!(w1b, "{:?}", w1a).unwrap();

        assert_eq!(w1a.buf, ['a', '\0', '\0']);
        assert_eq!(w1b.buf, ['"', 'a', '"']);

        write!(w2a, "a").unwrap();
        write!(w2b, "{:?}", w2a).unwrap();

        assert_eq!(w2a.buf, ['a', '\0', '\0', '\0', '\0']);
        assert_eq!(w2b.buf, ['"', 'a', '"', '\0', '\0']);
    }

    #[test]
    fn write() {
        let mut w1 = CharBufN1::new();
        let mut w2 = CharBufN2::new();

        write!(w1, "a").unwrap();
        write!(w2, "a").unwrap();

        assert_eq!(w1.buf, ['a', '\0', '\0']);
        assert_eq!(w1.len, 1);

        assert_eq!(w2.buf, ['a', '\0', '\0', '\0', '\0']);
        assert_eq!(w2.len, 1);

        write!(w1, "{:?}", [(); 0]).unwrap();
        write!(w2, "{:?}", [(); 0]).unwrap();

        assert_eq!(w1.buf, ['a', '[', ']']);
        assert_eq!(w1.len, 3);

        assert_eq!(w2.buf, ['a', '[', ']', '\0', '\0']);
        assert_eq!(w2.len, 3);

        let mut w1 = CharBufN1::new();
        let mut w2 = CharBufN2::new();

        write!(w1, "{:?}", [0]).unwrap();
        write!(w2, "{:?}", [0]).unwrap();

        assert_eq!(w1.buf, ['[', '0', ']']);
        assert_eq!(w1.len, 3);

        assert_eq!(w2.buf, ['[', '0', ']', '\0', '\0']);
        assert_eq!(w2.len, 3);

        assert_eq!(write!(w1, "!"), Err(fmt::Error));
        assert_eq!(write!(w2, "!"), Ok(()));

        assert_eq!(w1.buf, ['[', '0', ']']);
        assert_eq!(w1.len, 3);

        assert_eq!(w2.buf, ['[', '0', ']', '!', '\0']);
        assert_eq!(w2.len, 4);
    }

    #[test]
    fn eq() {
        let mut w1 = CharBufN1::new();
        let mut w2 = CharBufN2::new();

        write!(w1, "ab").unwrap();
        write!(w2, "ab").unwrap();

        assert_eq!(w1, "ab");
        assert_eq!(w1, *"ab");

        assert_eq!(w2, "ab");
        assert_eq!(w2, *"ab");

        assert_eq!(w1, w2);
        assert_eq!(w2, w1);

        write!(w2, "c").unwrap();

        assert_eq!("ab", w1);
        assert_eq!(*"ab", w1);

        assert_eq!("abc", w2);
        assert_eq!(*"abc", w2);

        assert_ne!(w1, w2);
        assert_ne!(w2, w1);

        assert_ne!(w1, "a");
        assert_ne!(w1, *"a");

        assert_ne!(w1, "abc");
        assert_ne!(w1, *"abc");
    }
}