fix: loading print panic
This commit is contained in:
123
nft/loading/loading.go
Normal file
123
nft/loading/loading.go
Normal file
@ -0,0 +1,123 @@
|
||||
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)
|
||||
|
||||
go func() {
|
||||
var (
|
||||
m *_msg
|
||||
ok bool
|
||||
processing string
|
||||
)
|
||||
|
||||
defer func() {
|
||||
fmt.Printf("\r\033[K")
|
||||
}()
|
||||
|
||||
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
|
||||
}
|
25
nft/loading/loading_test.go
Normal file
25
nft/loading/loading_test.go
Normal file
@ -0,0 +1,25 @@
|
||||
package loading
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestLoadingPrint(t *testing.T) {
|
||||
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
|
||||
defer cancel()
|
||||
|
||||
Do(ctx, func(ctx context.Context, print func(msg string, types ...Type)) error {
|
||||
print("start task 1...")
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
print("warning...1", TypeWarning)
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user