writer.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. package progress
  2. import (
  3. "context"
  4. "os"
  5. "sync"
  6. "time"
  7. "github.com/containerd/console"
  8. "github.com/moby/term"
  9. "golang.org/x/sync/errgroup"
  10. )
  11. // EventStatus indicates the status of an action
  12. type EventStatus int
  13. const (
  14. // Working means that the current task is working
  15. Working EventStatus = iota
  16. // Done means that the current task is done
  17. Done
  18. // Error means that the current task has errored
  19. Error
  20. )
  21. // Event reprensents a progress event
  22. type Event struct {
  23. ID string
  24. Text string
  25. Status EventStatus
  26. StatusText string
  27. Done bool
  28. startTime time.Time
  29. endTime time.Time
  30. spinner *spinner
  31. }
  32. func (e *Event) stop() {
  33. e.endTime = time.Now()
  34. e.spinner.Stop()
  35. }
  36. // Writer can write multiple progress events
  37. type Writer interface {
  38. Start(context.Context) error
  39. Stop()
  40. Event(Event)
  41. }
  42. type writerKey struct{}
  43. // WithContextWriter adds the writer to the context
  44. func WithContextWriter(ctx context.Context, writer Writer) context.Context {
  45. return context.WithValue(ctx, writerKey{}, writer)
  46. }
  47. // ContextWriter returns the writer from the context
  48. func ContextWriter(ctx context.Context) Writer {
  49. s, _ := ctx.Value(writerKey{}).(Writer)
  50. return s
  51. }
  52. type progressFunc func(context.Context) error
  53. // Run will run a writer and the progress function
  54. // in parallel
  55. func Run(ctx context.Context, pf progressFunc) error {
  56. eg, _ := errgroup.WithContext(ctx)
  57. w, err := NewWriter(os.Stderr)
  58. if err != nil {
  59. return err
  60. }
  61. eg.Go(func() error {
  62. return w.Start(context.Background())
  63. })
  64. ctx = WithContextWriter(ctx, w)
  65. eg.Go(func() error {
  66. defer w.Stop()
  67. return pf(ctx)
  68. })
  69. return eg.Wait()
  70. }
  71. // NewWriter returns a new multi-progress writer
  72. func NewWriter(out console.File) (Writer, error) {
  73. _, isTerminal := term.GetFdInfo(out)
  74. if isTerminal {
  75. con, err := console.ConsoleFromFile(out)
  76. if err != nil {
  77. return nil, err
  78. }
  79. return &ttyWriter{
  80. out: con,
  81. eventIDs: []string{},
  82. events: map[string]Event{},
  83. repeated: false,
  84. done: make(chan bool),
  85. mtx: &sync.RWMutex{},
  86. }, nil
  87. }
  88. return &plainWriter{
  89. out: out,
  90. done: make(chan bool),
  91. }, nil
  92. }