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  }