2023-03-03 19:52:34 +01:00
|
|
|
package statview
|
|
|
|
|
|
|
|
import (
|
2023-03-10 22:34:04 +01:00
|
|
|
"fmt"
|
2023-03-03 19:52:34 +01:00
|
|
|
"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]
|
2023-03-10 23:15:09 +01:00
|
|
|
doneCh chan struct{}
|
2023-03-03 19:52:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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),
|
2023-03-10 23:15:09 +01:00
|
|
|
doneCh: make(chan struct{}, 1),
|
2023-03-03 19:52:34 +01:00
|
|
|
lastReports: map[K]report[K]{},
|
|
|
|
wg: &sync.WaitGroup{},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *View[K]) Show() {
|
2023-03-10 22:34:04 +01:00
|
|
|
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()
|
|
|
|
}()
|
2023-03-03 19:52:34 +01:00
|
|
|
|
|
|
|
for status := range v.reportCh {
|
2023-03-10 22:58:13 +01:00
|
|
|
if _, ok := v.taskMap[status.ID]; !ok {
|
|
|
|
panic(fmt.Sprintf("received report for unknown task (forgot to call View.Add()?): %v", v))
|
|
|
|
}
|
|
|
|
|
2023-03-03 19:52:34 +01:00
|
|
|
v.lastReports[status.ID] = status
|
2023-03-06 14:09:42 +01:00
|
|
|
|
2023-03-10 22:34:04 +01:00
|
|
|
if printReports {
|
|
|
|
v.print()
|
|
|
|
}
|
|
|
|
}
|
2023-03-10 23:15:09 +01:00
|
|
|
|
|
|
|
v.doneCh <- struct{}{}
|
2023-03-03 19:52:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2023-03-10 22:34:04 +01:00
|
|
|
if _, ok := v.taskMap[task]; !ok {
|
|
|
|
panic(fmt.Sprintf("unreported task marked as done: %v", v))
|
|
|
|
}
|
2023-03-10 22:22:56 +01:00
|
|
|
|
2023-03-10 22:34:04 +01:00
|
|
|
v.wg.Done()
|
2023-03-06 13:24:06 +01:00
|
|
|
}
|
2023-03-10 23:15:09 +01:00
|
|
|
|
|
|
|
func (v *View[K]) WaitForAllTasks() {
|
|
|
|
<-v.doneCh
|
|
|
|
}
|