github.com/saracen/git-lfs@v2.5.2+incompatible/tasklog/percentage_task.go (about)

     1  package tasklog
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"sync/atomic"
     7  	"time"
     8  )
     9  
    10  // PercentageTask is a task that is performed against a known number of
    11  // elements.
    12  type PercentageTask struct {
    13  	// members managed via sync/atomic must be aligned at the top of this
    14  	// structure (see: https://github.com/git-lfs/git-lfs/pull/2880).
    15  
    16  	// n is the number of elements whose work has been completed. It is
    17  	// managed sync/atomic.
    18  	n uint64
    19  	// total is the total number of elements to execute work upon.
    20  	total uint64
    21  	// msg is the task message.
    22  	msg string
    23  	// ch is a channel which is written to when the task state changes and
    24  	// is closed when the task is completed.
    25  	ch chan *Update
    26  }
    27  
    28  func NewPercentageTask(msg string, total uint64) *PercentageTask {
    29  	p := &PercentageTask{
    30  		msg:   msg,
    31  		total: total,
    32  		ch:    make(chan *Update, 1),
    33  	}
    34  	p.Count(0)
    35  
    36  	return p
    37  }
    38  
    39  // Count indicates that work has been completed against "n" number of elements,
    40  // marking the task as complete if the total "n" given to all invocations of
    41  // this method is equal to total.
    42  //
    43  // Count returns the new total number of (atomically managed) elements that have
    44  // been completed.
    45  func (c *PercentageTask) Count(n uint64) (new uint64) {
    46  	if new = atomic.AddUint64(&c.n, n); new > c.total {
    47  		panic("tasklog: counted too many items")
    48  	}
    49  
    50  	var percentage float64
    51  	if c.total == 0 {
    52  		percentage = 100
    53  	} else {
    54  		percentage = 100 * float64(new) / float64(c.total)
    55  	}
    56  
    57  	c.ch <- &Update{
    58  		S: fmt.Sprintf("%s: %3.f%% (%d/%d)",
    59  			c.msg, math.Floor(percentage), new, c.total),
    60  		At: time.Now(),
    61  	}
    62  
    63  	if new >= c.total {
    64  		close(c.ch)
    65  	}
    66  
    67  	return new
    68  }
    69  
    70  // Entry logs a line-delimited task entry.
    71  func (t *PercentageTask) Entry(update string) {
    72  	t.ch <- &Update{
    73  		S:     fmt.Sprintf("%s\n", update),
    74  		At:    time.Now(),
    75  		Force: true,
    76  	}
    77  }
    78  
    79  // Updates implements Task.Updates and returns a channel which is written to
    80  // when the state of this task changes, and closed when the task is completed.
    81  // has been completed.
    82  func (c *PercentageTask) Updates() <-chan *Update {
    83  	return c.ch
    84  }
    85  
    86  // Throttled implements Task.Throttled and returns true, indicating that this
    87  // task is throttled.
    88  func (c *PercentageTask) Throttled() bool { return true }