package util import ( "io" "regexp" ) const ANSI_REGEX string = "\x1b[[0-9;]*m" // Strip ansi control characters from source // // The source reader is automatically closed when stripper reader is type AnsiFilterReadCloser struct { regex regexp.Regexp readCloser io.ReadCloser } func NewAnsiFilterReadCloser(readCloser io.ReadCloser) *AnsiFilterReadCloser { r := new(AnsiFilterReadCloser) r.regex = *regexp.MustCompile(ANSI_REGEX) r.readCloser = readCloser return r } func (rc AnsiFilterReadCloser) Read(p []byte) (int, error) { n, err := rc.readCloser.Read(p) if err != io.EOF && err != nil { return n, err } filtered := rc.regex.ReplaceAll(p[:n], []byte("")) n = copy(p, filtered) return n, err } func (rc AnsiFilterReadCloser) Close() error { return rc.readCloser.Close() } type AnsiFilterWriter struct { regex regexp.Regexp writer io.Writer } func NewAnsiFilterWriter(writer io.Writer) *AnsiFilterWriter { w := new(AnsiFilterWriter) w.regex = *regexp.MustCompile(ANSI_REGEX) w.writer = writer return w } func (w AnsiFilterWriter) Write(p []byte) (int, error) { filtered := w.regex.ReplaceAll(p, []byte("")) n, err := w.writer.Write(filtered) if n < len(filtered) { return n, io.ErrShortWrite } // NOTE: report the original size not the filtered size to indicate to caller // that input has been consumed return len(p), err }