...

Source file src/github.com/jedib0t/go-pretty/v6/progress/tracker.go

Documentation: github.com/jedib0t/go-pretty/v6/progress

     1  package progress
     2  
     3  import (
     4  	"math"
     5  	"sync"
     6  	"time"
     7  )
     8  
     9  // Tracker helps track the progress of a single task. The way to use it is to
    10  // instantiate a Tracker with a valid Message, a valid (expected) Total, and
    11  // Units values. This should then be fed to the Progress Writer with the
    12  // Writer.AppendTracker() method. When the task that is being done has progress,
    13  // increment the value using the Tracker.Increment(value) method.
    14  type Tracker struct {
    15  	// Message should contain a short description of the "task"; please note
    16  	// that this should NOT be updated in the middle of progress - you should
    17  	// instead use UpdateMessage() to do this safely without hitting any race
    18  	// conditions
    19  	Message string
    20  	// ExpectedDuration tells how long this task is expected to take; and will
    21  	// be used in calculation of the ETA value
    22  	ExpectedDuration time.Duration
    23  	// Total should be set to the (expected) Total/Final value to be reached
    24  	Total int64
    25  	// Units defines the type of the "value" being tracked
    26  	Units Units
    27  
    28  	done      bool
    29  	err       bool
    30  	mutex     sync.RWMutex
    31  	timeStart time.Time
    32  	timeStop  time.Time
    33  	value     int64
    34  }
    35  
    36  // ETA returns the expected time of "arrival" or completion of this tracker. It
    37  // is an estimate and is not guaranteed.
    38  func (t *Tracker) ETA() time.Duration {
    39  	t.mutex.RLock()
    40  	defer t.mutex.RUnlock()
    41  
    42  	timeTaken := time.Since(t.timeStart)
    43  	if t.ExpectedDuration > time.Duration(0) && t.ExpectedDuration > timeTaken {
    44  		return t.ExpectedDuration - timeTaken
    45  	}
    46  
    47  	pDone := int64(t.percentDoneWithoutLock())
    48  	if pDone == 0 {
    49  		return time.Duration(0)
    50  	}
    51  	return time.Duration((int64(timeTaken) / pDone) * (100 - pDone))
    52  }
    53  
    54  // Increment updates the current value of the task being tracked.
    55  func (t *Tracker) Increment(value int64) {
    56  	t.mutex.Lock()
    57  	t.incrementWithoutLock(value)
    58  	t.mutex.Unlock()
    59  }
    60  
    61  // IncrementWithError updates the current value of the task being tracked and
    62  // marks that an error occurred.
    63  func (t *Tracker) IncrementWithError(value int64) {
    64  	t.mutex.Lock()
    65  	t.incrementWithoutLock(value)
    66  	t.err = true
    67  	t.mutex.Unlock()
    68  }
    69  
    70  // IsDone returns true if the tracker is done (value has reached the expected
    71  // Total set during initialization).
    72  func (t *Tracker) IsDone() bool {
    73  	t.mutex.RLock()
    74  	defer t.mutex.RUnlock()
    75  
    76  	return t.done
    77  }
    78  
    79  // IsErrored true if an error was set with IncrementWithError or MarkAsErrored.
    80  func (t *Tracker) IsErrored() bool {
    81  	t.mutex.RLock()
    82  	defer t.mutex.RUnlock()
    83  
    84  	return t.err
    85  }
    86  
    87  // IsIndeterminate returns true if the tracker is indeterminate; i.e., the total
    88  // is unknown and it is impossible to auto-calculate if tracking is done.
    89  func (t *Tracker) IsIndeterminate() bool {
    90  	t.mutex.RLock()
    91  	defer t.mutex.RUnlock()
    92  
    93  	return t.Total == 0
    94  }
    95  
    96  // MarkAsDone forces completion of the tracker by updating the current value as
    97  // the expected Total value.
    98  func (t *Tracker) MarkAsDone() {
    99  	t.mutex.Lock()
   100  	t.Total = t.value
   101  	t.stop()
   102  	t.mutex.Unlock()
   103  }
   104  
   105  // MarkAsErrored forces completion of the tracker by updating the current value as
   106  // the expected Total value, and recording as error.
   107  func (t *Tracker) MarkAsErrored() {
   108  	t.mutex.Lock()
   109  	// only update error if not done and if not previously set
   110  	if !t.done {
   111  		t.Total = t.value
   112  		t.err = true
   113  		t.stop()
   114  	}
   115  	t.mutex.Unlock()
   116  }
   117  
   118  // PercentDone returns the currently completed percentage value.
   119  func (t *Tracker) PercentDone() float64 {
   120  	t.mutex.RLock()
   121  	defer t.mutex.RUnlock()
   122  	return t.percentDoneWithoutLock()
   123  }
   124  
   125  func (t *Tracker) percentDoneWithoutLock() float64 {
   126  	if t.Total == 0 {
   127  		return 0
   128  	}
   129  	return float64(t.value) * 100.0 / float64(t.Total)
   130  }
   131  
   132  // Reset resets the tracker to its initial state.
   133  func (t *Tracker) Reset() {
   134  	t.mutex.Lock()
   135  	t.done = false
   136  	t.err = false
   137  	t.timeStart = time.Time{}
   138  	t.timeStop = time.Time{}
   139  	t.value = 0
   140  	t.mutex.Unlock()
   141  }
   142  
   143  // SetValue sets the value of the tracker and re-calculates if the tracker is
   144  // "done".
   145  func (t *Tracker) SetValue(value int64) {
   146  	t.mutex.Lock()
   147  	t.done = false
   148  	t.timeStop = time.Time{}
   149  	t.value = 0
   150  	t.incrementWithoutLock(value)
   151  	t.mutex.Unlock()
   152  }
   153  
   154  // UpdateMessage updates the message string.
   155  func (t *Tracker) UpdateMessage(msg string) {
   156  	t.mutex.Lock()
   157  	t.Message = msg
   158  	t.mutex.Unlock()
   159  }
   160  
   161  // Value returns the current value of the tracker.
   162  func (t *Tracker) Value() int64 {
   163  	t.mutex.RLock()
   164  	defer t.mutex.RUnlock()
   165  	return t.value
   166  }
   167  
   168  func (t *Tracker) message() string {
   169  	t.mutex.RLock()
   170  	defer t.mutex.RUnlock()
   171  	return t.Message
   172  }
   173  
   174  func (t *Tracker) valueAndTotal() (int64, int64) {
   175  	t.mutex.RLock()
   176  	value := t.value
   177  	total := t.Total
   178  	t.mutex.RUnlock()
   179  	return value, total
   180  }
   181  
   182  func (t *Tracker) incrementWithoutLock(value int64) {
   183  	if !t.done {
   184  		t.value += value
   185  		if t.Total > 0 && t.value >= t.Total {
   186  			t.stop()
   187  		}
   188  	}
   189  }
   190  
   191  func (t *Tracker) start() {
   192  	t.mutex.Lock()
   193  	if t.Total < 0 {
   194  		t.Total = math.MaxInt64
   195  	}
   196  	t.done = false
   197  	t.err = false
   198  	t.timeStart = time.Now()
   199  	t.mutex.Unlock()
   200  }
   201  
   202  // this must be called with the mutex held with a write lock
   203  func (t *Tracker) stop() {
   204  	t.done = true
   205  	t.timeStop = time.Now()
   206  	if t.value > t.Total {
   207  		t.Total = t.value
   208  	}
   209  }
   210  

View as plain text