statview/view.go

107 lines
1.8 KiB
Go

package statview
import (
"fmt"
"io"
"os"
"sort"
"sync"
)
type View[K comparable] struct {
writer io.Writer
sortFunc func(i, j K) bool
lastLineAmount int
lastReports map[K]report[K]
tasks []K
taskMap map[K]struct{}
wg *sync.WaitGroup
reportCh chan report[K]
doneCh chan struct{}
}
func New[K comparable]() *View[K] {
return NewForWriter[K](os.Stderr)
}
func NewForWriter[K comparable](w io.Writer) *View[K] {
return &View[K]{
writer: w,
tasks: make([]K, 0),
taskMap: map[K]struct{}{},
reportCh: make(chan report[K], 100),
doneCh: make(chan struct{}, 1),
lastReports: map[K]report[K]{},
wg: &sync.WaitGroup{},
}
}
func (v *View[K]) Show() {
v.consumeReports(true)
}
func (v *View[K]) Hide() {
v.consumeReports(false)
}
func (v *View[K]) consumeReports(printReports bool) {
go func() {
defer close(v.reportCh)
v.wg.Wait()
}()
for status := range v.reportCh {
if _, ok := v.taskMap[status.ID]; !ok {
panic(fmt.Sprintf("received report for unknown task (forgot to call View.Add()?): %v", v))
}
v.lastReports[status.ID] = status
if printReports {
v.print()
}
}
v.doneCh <- struct{}{}
}
func (v *View[K]) SortFunc(f func(i, j K) bool) {
v.sortFunc = f
}
func (v *View[K]) Add(task K) {
if _, ok := v.taskMap[task]; ok {
return
}
v.wg.Add(1)
v.tasks = append(v.tasks, task)
v.taskMap[task] = struct{}{}
if v.sortFunc != nil {
sort.Slice(v.tasks, func(i, j int) bool {
return v.sortFunc(v.tasks[i], v.tasks[j])
})
}
}
func (v *View[K]) Report(task K, status string) {
v.reportCh <- report[K]{ID: task, Text: status}
}
func (v *View[K]) Done(task K) {
if _, ok := v.taskMap[task]; !ok {
panic(fmt.Sprintf("unreported task marked as done: %v", v))
}
v.wg.Done()
}
func (v *View[K]) WaitForAllTasks() {
<-v.doneCh
}