noansi 0.1.1

Strip ANSI codes from strings.
Documentation

use std::{fmt::{Display, Formatter}};
use vte::{Parser, Perform};

#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Noansi<T: AsRef<str>>(pub T);

struct AnsiStripper<'a, 'f, const BUFFER_SIZE: usize> {
    formatter: &'a mut Formatter<'f>,
    buffer: [u8; BUFFER_SIZE],
    len: usize,
}

impl<'a, 'f, const BUFFER_SIZE: usize> AnsiStripper<'a, 'f, BUFFER_SIZE> {
    #[must_use]
    #[inline(always)]
    const fn new(formatter: &'a mut Formatter<'f>) -> Self {
        Self {
            formatter,
            buffer: [0u8; BUFFER_SIZE],
            len: 0usize,
        }
    }

    fn flush(&mut self) -> std::fmt::Result {
        if self.len == 0 {
            return Ok(());
        }
        let raw_ptr = self.buffer.as_ptr();
        let len = self.len;
        let slice = unsafe { std::slice::from_raw_parts(raw_ptr, len) };
        let s = unsafe { str::from_utf8_unchecked(slice) };
        self.len = 0;
        self.formatter.write_str(s)
    }

    fn putchr(&mut self, chr: char) -> std::fmt::Result {
        let len_utf8 = chr.len_utf8();
        if (BUFFER_SIZE - self.len) < len_utf8 {
            self.flush()?;
        }
        let buf = &mut self.buffer[self.len..];
        chr.encode_utf8(buf);
        self.len += len_utf8;
        Ok(())
    }
}

impl<'a, 'f, const BUFFER_SIZE: usize> Perform for AnsiStripper<'a, 'f, BUFFER_SIZE> {
    fn print(&mut self, c: char) {
        self.putchr(c).expect("Failed to put the char.")
    }
}

impl<T: AsRef<str>> Display for Noansi<T> {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        let mut stripper: AnsiStripper<4096> = AnsiStripper::new(f);
        _=Parser::new().advance_until_terminated(&mut stripper, self.0.as_ref().as_bytes());
        stripper.flush()?;
        Ok(())
    }
}