124 lines
1.8 KiB
Go
124 lines
1.8 KiB
Go
|
package loading
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"fmt"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
type Type int
|
||
|
|
||
|
const (
|
||
|
TypeProcessing Type = iota
|
||
|
TypeInfo
|
||
|
TypeSuccess
|
||
|
TypeWarning
|
||
|
TypeError
|
||
|
)
|
||
|
|
||
|
func (t Type) Symbol() string {
|
||
|
switch t {
|
||
|
case TypeSuccess:
|
||
|
return "✔️ "
|
||
|
case TypeWarning:
|
||
|
return "❗ "
|
||
|
case TypeError:
|
||
|
return "❌ "
|
||
|
case TypeInfo:
|
||
|
return "❕ "
|
||
|
default:
|
||
|
return ""
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type _msg struct {
|
||
|
msg string
|
||
|
t Type
|
||
|
}
|
||
|
|
||
|
var frames = []string{"|", "/", "-", "\\"}
|
||
|
|
||
|
func Do(ctx context.Context, fn func(ctx context.Context, print func(msg string, types ...Type)) error) (err error) {
|
||
|
start := time.Now()
|
||
|
ch := make(chan *_msg)
|
||
|
|
||
|
defer func() {
|
||
|
fmt.Printf("\r\033[K")
|
||
|
}()
|
||
|
|
||
|
go func() {
|
||
|
var (
|
||
|
m *_msg
|
||
|
ok bool
|
||
|
processing string
|
||
|
)
|
||
|
|
||
|
for {
|
||
|
for _, frame := range frames {
|
||
|
select {
|
||
|
case <-ctx.Done():
|
||
|
return
|
||
|
case m, ok = <-ch:
|
||
|
if !ok || m == nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
switch m.t {
|
||
|
case TypeProcessing:
|
||
|
if m.msg != "" {
|
||
|
processing = m.msg
|
||
|
}
|
||
|
case TypeInfo,
|
||
|
TypeSuccess,
|
||
|
TypeWarning,
|
||
|
TypeError:
|
||
|
// Clear the loading animation
|
||
|
fmt.Printf("\r\033[K")
|
||
|
fmt.Printf("%s%s\n", m.t.Symbol(), m.msg)
|
||
|
}
|
||
|
default:
|
||
|
elapsed := time.Since(start).Seconds()
|
||
|
if processing != "" {
|
||
|
fmt.Printf("\r\033[K%s %s (%.2fs)", frame, processing, elapsed)
|
||
|
}
|
||
|
time.Sleep(100 * time.Millisecond)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
printFn := func(msg string, types ...Type) {
|
||
|
if msg == "" {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
m := &_msg{
|
||
|
msg: msg,
|
||
|
t: TypeProcessing,
|
||
|
}
|
||
|
|
||
|
if len(types) > 0 {
|
||
|
m.t = types[0]
|
||
|
}
|
||
|
|
||
|
ch <- m
|
||
|
}
|
||
|
|
||
|
done := make(chan struct{})
|
||
|
go func() {
|
||
|
if err = fn(ctx, printFn); err != nil {
|
||
|
ch <- &_msg{msg: err.Error(), t: TypeError}
|
||
|
}
|
||
|
|
||
|
close(ch)
|
||
|
done <- struct{}{}
|
||
|
}()
|
||
|
|
||
|
select {
|
||
|
case <-ctx.Done():
|
||
|
case <-done:
|
||
|
}
|
||
|
|
||
|
return err
|
||
|
}
|