Skip to content

Instantly share code, notes, and snippets.

@jakoblorz
Created August 2, 2019 22:12
Show Gist options
  • Save jakoblorz/217faf348615c8484fa264b9ebc7e601 to your computer and use it in GitHub Desktop.
Save jakoblorz/217faf348615c8484fa264b9ebc7e601 to your computer and use it in GitHub Desktop.
Simple jest-like output for golang
package main
import (
"encoding/hex"
"fmt"
"io"
"os"
"os/signal"
"runtime"
"sync"
"time"
"unicode/utf8"
"github.com/fatih/color"
)
type EraseableWriter struct {
w io.Writer
d []byte
l sync.Mutex
}
func (e *EraseableWriter) Write(p []byte) (n int, err error) {
n, err = e.w.Write(p)
d := e.d
e.d = make([]byte, len(d)+n)
i := 0
for _, b := range d {
e.d[i] = b
i++
}
for _, b := range p {
e.d[i] = b
i++
}
return n, err
}
func (e *EraseableWriter) Erase() {
e.l.Lock()
defer e.l.Unlock()
n := utf8.RuneCount(e.d)
if runtime.GOOS == "windows" {
clearString := ""
for i := 0; i < n; i++ {
clearString += " "
}
clearString += "\r"
fmt.Fprintf(e.w, clearString)
} else {
del, _ := hex.DecodeString("7f")
for _, c := range []string{
"\b",
string(del),
"\b",
"\033[K", // for macOS Terminal
"\u001b[2J",
"\u001b[0;0H",
} {
for i := 0; i < n; i++ {
fmt.Fprintf(e.w, c)
}
}
}
e.d = make([]byte, 0)
}
type NotifyableWriter struct {
C chan []byte
}
func (n *NotifyableWriter) Write(p []byte) (int, error) {
n.C <- p
return len(p), nil
}
type Status struct {
sync.Mutex
Characters map[string]string
c string
Status string
Prefix string
Message string
}
func (s *Status) Fprint(w io.Writer) (n int, err error) {
s.Lock()
defer s.Unlock()
status := fmt.Sprintf("%s %s", s.c, s.Status)
if !(runtime.GOOS == "windows" && w == os.Stderr) {
switch s.Status {
case FAIL:
status = fmt.Sprintf(" %s", B_RED.Sprintf("%s", s.Status))
break
case RUN:
s.c = s.Characters[s.c]
status = fmt.Sprintf("%s %s", s.c, B_CYAN.Sprintf("%s", s.Status))
break
case DONE:
status = fmt.Sprintf(" %s", B_GREEN.Sprintf("%s", s.Status))
break
default:
}
}
if runtime.GOOS == "windows" {
status = fmt.Sprintf("\r%s %s\n", status, s.Message)
} else {
status = fmt.Sprintf("%s %s\n", status, s.Message)
}
return fmt.Fprint(w, status)
}
var (
RUN = " RUN "
FAIL = " FAIL "
DONE = " DONE "
B_CYAN = color.New(color.BgHiYellow).Add(color.Bold).Add(color.FgBlack)
B_RED = color.New(color.BgRed).Add(color.Bold).Add(color.FgWhite)
B_GREEN = color.New(color.BgGreen).Add(color.Bold).Add(color.FgWhite)
char_m = make(map[string]string)
ss []*Status
)
func init() {
chars := []string{"▖", "▘", "▝", "▗"}
char_m[""] = chars[0]
for i := 0; i < len(chars)-1; i++ {
char_m[chars[i]] = chars[i+1]
}
char_m[chars[len(chars)-1]] = chars[0]
ss = []*Status{
&Status{
Characters: char_m,
Mutex: sync.Mutex{},
Prefix: "",
Status: RUN,
Message: "github.com/test-one",
},
&Status{
Characters: char_m,
Mutex: sync.Mutex{},
Prefix: "",
Status: FAIL,
Message: "github.com/test-one",
},
&Status{
Characters: char_m,
Mutex: sync.Mutex{},
Prefix: "",
Status: DONE,
Message: "github.com/test-one",
},
}
}
func main() {
writer := &EraseableWriter{
w: os.Stdout,
d: make([]byte, 0),
l: sync.Mutex{},
}
sink := &NotifyableWriter{
C: make(chan []byte, 0),
}
reader := os.Stdin
readerCh := sink.C
go func() {
io.Copy(sink, reader)
}()
cancelCh := make(chan os.Signal, 1)
signal.Notify(cancelCh, os.Interrupt)
ticker := time.NewTicker(100 * time.Millisecond)
defer ticker.Stop()
tickerCh := ticker.C
var c string
for {
c = char_m[c]
select {
case <-cancelCh:
fmt.Print("\n")
return
case <-readerCh:
writer.Erase()
case <-tickerCh:
writer.Erase()
for _, s := range ss {
s.Fprint(writer)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment