github.com/vmware/govmomi@v0.51.0/vim25/progress/loger.go (about) 1 // © Broadcom. All Rights Reserved. 2 // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. 3 // SPDX-License-Identifier: Apache-2.0 4 5 package progress 6 7 import ( 8 "fmt" 9 "io" 10 "sync" 11 "time" 12 ) 13 14 type LogFunc func(msg string) (int, error) 15 16 type ProgressLogger struct { 17 log LogFunc 18 prefix string 19 20 wg sync.WaitGroup 21 22 sink chan chan Report 23 done chan struct{} 24 } 25 26 func NewProgressLogger(log LogFunc, prefix string) *ProgressLogger { 27 p := &ProgressLogger{ 28 log: log, 29 prefix: prefix, 30 31 sink: make(chan chan Report), 32 done: make(chan struct{}), 33 } 34 35 p.wg.Add(1) 36 37 go p.loopA() 38 39 return p 40 } 41 42 // loopA runs before Sink() has been called. 43 func (p *ProgressLogger) loopA() { 44 var err error 45 46 defer p.wg.Done() 47 48 tick := time.NewTicker(100 * time.Millisecond) 49 defer tick.Stop() 50 51 called := false 52 53 for stop := false; !stop; { 54 select { 55 case ch := <-p.sink: 56 err = p.loopB(tick, ch) 57 stop = true 58 called = true 59 case <-p.done: 60 stop = true 61 case <-tick.C: 62 line := fmt.Sprintf("\r%s", p.prefix) 63 p.log(line) 64 } 65 } 66 67 if err != nil && err != io.EOF { 68 p.log(fmt.Sprintf("\r%sError: %s\n", p.prefix, err)) 69 } else if called { 70 p.log(fmt.Sprintf("\r%sOK\n", p.prefix)) 71 } 72 } 73 74 // loopA runs after Sink() has been called. 75 func (p *ProgressLogger) loopB(tick *time.Ticker, ch <-chan Report) error { 76 var r Report 77 var ok bool 78 var err error 79 80 for ok = true; ok; { 81 select { 82 case r, ok = <-ch: 83 if !ok { 84 break 85 } 86 err = r.Error() 87 case <-tick.C: 88 line := fmt.Sprintf("\r%s", p.prefix) 89 if r != nil { 90 line += fmt.Sprintf("(%.0f%%", r.Percentage()) 91 detail := r.Detail() 92 if detail != "" { 93 line += fmt.Sprintf(", %s", detail) 94 } 95 line += ")" 96 } 97 p.log(line) 98 } 99 } 100 101 return err 102 } 103 104 func (p *ProgressLogger) Sink() chan<- Report { 105 ch := make(chan Report) 106 p.sink <- ch 107 return ch 108 } 109 110 func (p *ProgressLogger) Wait() { 111 close(p.done) 112 p.wg.Wait() 113 }