91 lines
2.8 KiB
Go
91 lines
2.8 KiB
Go
|
package progress
|
||
|
|
||
|
import (
|
||
|
"io"
|
||
|
"strings"
|
||
|
"sync/atomic"
|
||
|
|
||
|
"github.com/pterodactyl/wings/system"
|
||
|
)
|
||
|
|
||
|
// Progress is used to track the progress of any I/O operation that are being
|
||
|
// performed.
|
||
|
type Progress struct {
|
||
|
// written is the total size of the files that have been written to the writer.
|
||
|
written uint64
|
||
|
// Total is the total size of the archive in bytes.
|
||
|
total uint64
|
||
|
|
||
|
// Writer .
|
||
|
Writer io.Writer
|
||
|
}
|
||
|
|
||
|
// NewProgress returns a new progress tracker for the given total size.
|
||
|
func NewProgress(total uint64) *Progress {
|
||
|
return &Progress{total: total}
|
||
|
}
|
||
|
|
||
|
// Written returns the total number of bytes written.
|
||
|
// This function should be used when the progress is tracking data being written.
|
||
|
func (p *Progress) Written() uint64 {
|
||
|
return atomic.LoadUint64(&p.written)
|
||
|
}
|
||
|
|
||
|
// Total returns the total size in bytes.
|
||
|
func (p *Progress) Total() uint64 {
|
||
|
return atomic.LoadUint64(&p.total)
|
||
|
}
|
||
|
|
||
|
// SetTotal sets the total size of the archive in bytes. This function is safe
|
||
|
// to call concurrently and can be used to update the total size if it changes,
|
||
|
// such as when the total size is simultaneously being calculated as data is
|
||
|
// being written through the progress writer.
|
||
|
func (p *Progress) SetTotal(total uint64) {
|
||
|
atomic.StoreUint64(&p.total, total)
|
||
|
}
|
||
|
|
||
|
// Write totals the number of bytes that have been written to the writer.
|
||
|
func (p *Progress) Write(v []byte) (int, error) {
|
||
|
n := len(v)
|
||
|
atomic.AddUint64(&p.written, uint64(n))
|
||
|
if p.Writer != nil {
|
||
|
return p.Writer.Write(v)
|
||
|
}
|
||
|
return n, nil
|
||
|
}
|
||
|
|
||
|
// Progress returns a formatted progress string for the current progress.
|
||
|
func (p *Progress) Progress(width int) string {
|
||
|
// current = 100 (Progress, dynamic)
|
||
|
// total = 1000 (Content-Length, dynamic)
|
||
|
// width = 25 (Number of ticks to display, static)
|
||
|
// widthPercentage = 100 / width (What percentage does each tick represent, static)
|
||
|
//
|
||
|
// percentageDecimal = current / total = 0.1
|
||
|
// percentage = percentageDecimal * 100 = 10%
|
||
|
// ticks = percentage / widthPercentage = 2.5
|
||
|
//
|
||
|
// ticks is a float64, so we cast it to an int which rounds it down to 2.
|
||
|
|
||
|
// Values are cast to floats to prevent integer division.
|
||
|
current := p.Written()
|
||
|
total := p.Total()
|
||
|
// width := is passed as a parameter
|
||
|
widthPercentage := float64(100) / float64(width)
|
||
|
percentageDecimal := float64(current) / float64(total)
|
||
|
percentage := percentageDecimal * 100
|
||
|
ticks := int(percentage / widthPercentage)
|
||
|
|
||
|
// Ensure that we never get a negative number of ticks, this will prevent strings#Repeat
|
||
|
// from panicking. A negative number of ticks is likely to happen when the total size is
|
||
|
// inaccurate, such as when we are going off of rough disk usage calculation.
|
||
|
if ticks < 0 {
|
||
|
ticks = 0
|
||
|
} else if ticks > width {
|
||
|
ticks = width
|
||
|
}
|
||
|
|
||
|
bar := strings.Repeat("=", ticks) + strings.Repeat(" ", width-ticks)
|
||
|
return "[" + bar + "] " + system.FormatBytes(current) + " / " + system.FormatBytes(total)
|
||
|
}
|