statusbar.go (1288B)
1 package statusbar 2 3 import ( 4 "context" 5 "fmt" 6 "strings" 7 "sync" 8 "time" 9 10 "code.dwrz.net/src/pkg/log" 11 ) 12 13 type Block interface { 14 Name() string 15 Render(ctx context.Context) (string, error) 16 } 17 18 type Parameters struct { 19 Blocks []Block 20 Log *log.Logger 21 Separator string 22 } 23 24 type StatusBar struct { 25 b *strings.Builder 26 blocks []Block 27 l *log.Logger 28 sep string 29 } 30 31 func (s *StatusBar) Render(ctx context.Context) string { 32 defer s.b.Reset() 33 34 fmt.Fprintf(s.b, "%s ", s.sep) 35 36 var ( 37 timeout, cancel = context.WithTimeout(ctx, 100*time.Millisecond) 38 outputs = make([]string, len(s.blocks)) 39 wg sync.WaitGroup 40 ) 41 defer cancel() 42 43 wg.Add(len(s.blocks)) 44 45 for i, b := range s.blocks { 46 go func(i int, b Block) { 47 defer wg.Done() 48 49 text, err := b.Render(timeout) 50 if err != nil { 51 s.l.Error.Printf( 52 "failed to render %s: %v", 53 b.Name(), err, 54 ) 55 outputs[i] = "" 56 } else { 57 outputs[i] = text 58 } 59 }(i, b) 60 } 61 62 wg.Wait() 63 64 for i, o := range outputs { 65 s.b.WriteString(o) 66 67 if i < len(outputs)-1 { 68 fmt.Fprintf(s.b, " %s ", s.sep) 69 } 70 } 71 72 return s.b.String() 73 } 74 75 func New(p Parameters) *StatusBar { 76 return &StatusBar{ 77 b: &strings.Builder{}, 78 blocks: p.Blocks, 79 l: p.Log, 80 sep: p.Separator, 81 } 82 }