update c/image

* progress bar: use spinners for unknown blob sizes
 * improve README.md and the review of the changes
 * use 'containers_image_ostree' as build tag
 * ostree: default is no OStree support
 * Add "Env" to ImageInspectInfo
 * config.go: improve debug message
 * config.go: log where credentials come from

Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
This commit is contained in:
Valentin Rothberg
2019-07-17 08:05:37 +02:00
parent 5c1ce1e033
commit 36723bc118
33 changed files with 490 additions and 325 deletions

View File

@@ -1,8 +1,8 @@
language: go
sudo: false
go:
- 1.8.x
- 1.9.x
- 1.10.x
- tip
before_install:
- go get -t -v ./...

View File

@@ -31,8 +31,6 @@ _Note:_ it is preferable to go get from github.com, rather than gopkg.in. See is
p := mpb.New(
// override default (80) width
mpb.WithWidth(64),
// override default "[=>-]" format
mpb.WithFormat("╢▌▌░╟"),
// override default 120ms refresh rate
mpb.WithRefreshRate(180*time.Millisecond),
)
@@ -41,6 +39,8 @@ _Note:_ it is preferable to go get from github.com, rather than gopkg.in. See is
name := "Single Bar:"
// adding a single bar
bar := p.AddBar(int64(total),
// override default "[=>-]" style
mpb.BarStyle("╢▌▌░╟"),
mpb.PrependDecorators(
// display our name with one space on the right
decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}),

View File

@@ -2,6 +2,7 @@ package mpb
import (
"bytes"
"context"
"fmt"
"io"
"io/ioutil"
@@ -11,21 +12,8 @@ import (
"unicode/utf8"
"github.com/vbauerster/mpb/decor"
"github.com/vbauerster/mpb/internal"
)
const (
rLeft = iota
rFill
rTip
rEmpty
rRight
)
const formatLen = 5
type barRunes [formatLen]rune
// Bar represents a progress Bar
type Bar struct {
priority int
@@ -45,15 +33,30 @@ type Bar struct {
shutdown chan struct{}
}
// Filler interface.
// Bar renders by calling Filler's Fill method. You can literally have
// any bar kind, by implementing this interface and passing it to the
// Add method.
type Filler interface {
Fill(w io.Writer, width int, s *decor.Statistics)
}
// FillerFunc is function type adapter to convert function into Filler.
type FillerFunc func(w io.Writer, width int, stat *decor.Statistics)
func (f FillerFunc) Fill(w io.Writer, width int, stat *decor.Statistics) {
f(w, width, stat)
}
type (
bState struct {
filler Filler
id int
width int
alignment int
total int64
current int64
runes barRunes
trimLeftSpace bool
trimRightSpace bool
trimSpace bool
toComplete bool
removeOnComplete bool
barClearOnComplete bool
@@ -73,8 +76,8 @@ type (
runningBar *Bar
}
refill struct {
char rune
till int64
r rune
limit int64
}
frameReader struct {
io.Reader
@@ -84,14 +87,20 @@ type (
}
)
func newBar(wg *sync.WaitGroup, id int, total int64, cancel <-chan struct{}, options ...BarOption) *Bar {
if total <= 0 {
total = time.Now().Unix()
}
func newBar(
ctx context.Context,
wg *sync.WaitGroup,
filler Filler,
id, width int,
total int64,
options ...BarOption,
) *Bar {
s := &bState{
filler: filler,
id: id,
priority: id,
width: width,
total: total,
}
@@ -104,6 +113,9 @@ func newBar(wg *sync.WaitGroup, id int, total int64, cancel <-chan struct{}, opt
s.bufP = bytes.NewBuffer(make([]byte, 0, s.width))
s.bufB = bytes.NewBuffer(make([]byte, 0, s.width))
s.bufA = bytes.NewBuffer(make([]byte, 0, s.width))
if s.newLineExtendFn != nil {
s.bufNL = bytes.NewBuffer(make([]byte, 0, s.width))
}
b := &Bar{
priority: s.priority,
@@ -121,11 +133,7 @@ func newBar(wg *sync.WaitGroup, id int, total int64, cancel <-chan struct{}, opt
b.priority = b.runningBar.priority
}
if s.newLineExtendFn != nil {
s.bufNL = bytes.NewBuffer(make([]byte, 0, s.width))
}
go b.serve(wg, s, cancel)
go b.serve(ctx, wg, s)
return b
}
@@ -178,52 +186,42 @@ func (b *Bar) Current() int64 {
}
// SetTotal sets total dynamically.
// Set final to true, when total is known, it will trigger bar complete event.
func (b *Bar) SetTotal(total int64, final bool) bool {
// Set complete to true, to trigger bar complete event now.
func (b *Bar) SetTotal(total int64, complete bool) {
select {
case b.operateState <- func(s *bState) {
if total > 0 {
s.total = total
}
if final {
s.total = total
if complete && !s.toComplete {
s.current = s.total
s.toComplete = true
}
}:
return true
case <-b.done:
return false
}
}
// SetRefill sets fill rune to r, up until n.
func (b *Bar) SetRefill(n int, r rune) {
if n <= 0 {
return
}
// SetRefill sets refill, if supported by underlying Filler.
func (b *Bar) SetRefill(amount int64) {
b.operateState <- func(s *bState) {
s.refill = &refill{r, int64(n)}
if f, ok := s.filler.(interface{ SetRefill(int64) }); ok {
f.SetRefill(amount)
}
}
}
// RefillBy is deprecated, use SetRefill
func (b *Bar) RefillBy(n int, r rune) {
b.SetRefill(n, r)
}
// Increment is a shorthand for b.IncrBy(1).
func (b *Bar) Increment() {
b.IncrBy(1)
}
// IncrBy increments progress bar by amount of n.
// wdd is optional work duration i.e. time.Since(start),
// which expected to be provided, if any ewma based decorator is used.
// wdd is optional work duration i.e. time.Since(start), which expected
// to be provided, if any ewma based decorator is used.
func (b *Bar) IncrBy(n int, wdd ...time.Duration) {
select {
case b.operateState <- func(s *bState) {
s.current += int64(n)
if s.current >= s.total {
if s.total > 0 && s.current >= s.total {
s.current = s.total
s.toComplete = true
}
@@ -238,9 +236,9 @@ func (b *Bar) IncrBy(n int, wdd ...time.Duration) {
// Completed reports whether the bar is in completed state.
func (b *Bar) Completed() bool {
// omit select here, because primary usage of the method is for loop
// condition, like for !bar.Completed() {...}
// so when toComplete=true it is called once (at which time, the bar is still alive),
// then quits the loop and never suppose to be called afterwards.
// condition, like for !bar.Completed() {...} so when toComplete=true
// it is called once (at which time, the bar is still alive), then
// quits the loop and never suppose to be called afterwards.
return <-b.boolCh
}
@@ -253,8 +251,9 @@ func (b *Bar) wSyncTable() [][]chan int {
}
}
func (b *Bar) serve(wg *sync.WaitGroup, s *bState, cancel <-chan struct{}) {
func (b *Bar) serve(ctx context.Context, wg *sync.WaitGroup, s *bState) {
defer wg.Done()
cancel := ctx.Done()
for {
select {
case op := <-b.operateState:
@@ -322,8 +321,6 @@ func (b *Bar) render(debugOut io.Writer, tw int) {
}
func (s *bState) draw(termWidth int) io.Reader {
defer s.bufA.WriteByte('\n')
if s.panicMsg != "" {
return strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", termWidth), s.panicMsg))
}
@@ -338,77 +335,32 @@ func (s *bState) draw(termWidth int) io.Reader {
s.bufA.WriteString(d.Decor(stat))
}
prependCount := utf8.RuneCount(s.bufP.Bytes())
appendCount := utf8.RuneCount(s.bufA.Bytes())
if s.barClearOnComplete && s.completeFlushed {
s.bufA.WriteByte('\n')
return io.MultiReader(s.bufP, s.bufA)
}
s.fillBar(s.width)
barCount := utf8.RuneCount(s.bufB.Bytes())
totalCount := prependCount + barCount + appendCount
if spaceCount := 0; totalCount > termWidth {
if !s.trimLeftSpace {
spaceCount++
}
if !s.trimRightSpace {
spaceCount++
}
s.fillBar(termWidth - prependCount - appendCount - spaceCount)
}
prependCount := utf8.RuneCount(s.bufP.Bytes())
appendCount := utf8.RuneCount(s.bufA.Bytes())
return io.MultiReader(s.bufP, s.bufB, s.bufA)
}
func (s *bState) fillBar(width int) {
defer func() {
s.bufB.WriteRune(s.runes[rRight])
if !s.trimRightSpace {
s.bufB.WriteByte(' ')
}
}()
s.bufB.Reset()
if !s.trimLeftSpace {
if !s.trimSpace {
// reserve space for edge spaces
termWidth -= 2
s.bufB.WriteByte(' ')
}
s.bufB.WriteRune(s.runes[rLeft])
if width <= 2 {
return
}
// bar s.width without leftEnd and rightEnd runes
barWidth := width - 2
completedWidth := internal.Percentage(s.total, s.current, int64(barWidth))
if s.refill != nil {
till := internal.Percentage(s.total, s.refill.till, int64(barWidth))
// append refill rune
var i int64
for i = 0; i < till; i++ {
s.bufB.WriteRune(s.refill.char)
}
for i = till; i < completedWidth; i++ {
s.bufB.WriteRune(s.runes[rFill])
}
if prependCount+s.width+appendCount > termWidth {
s.filler.Fill(s.bufB, termWidth-prependCount-appendCount, stat)
} else {
var i int64
for i = 0; i < completedWidth; i++ {
s.bufB.WriteRune(s.runes[rFill])
}
s.filler.Fill(s.bufB, s.width, stat)
}
if completedWidth < int64(barWidth) && completedWidth > 0 {
_, size := utf8.DecodeLastRune(s.bufB.Bytes())
s.bufB.Truncate(s.bufB.Len() - size)
s.bufB.WriteRune(s.runes[rTip])
if !s.trimSpace {
s.bufB.WriteByte(' ')
}
for i := completedWidth; i < int64(barWidth); i++ {
s.bufB.WriteRune(s.runes[rEmpty])
}
s.bufA.WriteByte('\n')
return io.MultiReader(s.bufP, s.bufB, s.bufA)
}
func (s *bState) wSyncTable() [][]chan int {
@@ -442,14 +394,6 @@ func newStatistics(s *bState) *decor.Statistics {
}
}
func strToBarRunes(format string) (array barRunes) {
for i, n := 0, 0; len(format) > 0; i++ {
array[i], n = utf8.DecodeRuneInString(format)
format = format[n:]
}
return
}
func countLines(b []byte) int {
return bytes.Count(b, []byte("\n"))
}

111
vendor/github.com/vbauerster/mpb/bar_filler.go generated vendored Normal file
View File

@@ -0,0 +1,111 @@
package mpb
import (
"io"
"unicode/utf8"
"github.com/vbauerster/mpb/decor"
"github.com/vbauerster/mpb/internal"
)
const (
rLeft = iota
rFill
rTip
rEmpty
rRight
rRevTip
rRefill
)
var defaultBarStyle = "[=>-]<+"
type barFiller struct {
format [][]byte
refillAmount int64
reverse bool
}
func newDefaultBarFiller() Filler {
bf := &barFiller{
format: make([][]byte, utf8.RuneCountInString(defaultBarStyle)),
}
bf.setStyle(defaultBarStyle)
return bf
}
func (s *barFiller) setStyle(style string) {
if !utf8.ValidString(style) {
return
}
src := make([][]byte, 0, utf8.RuneCountInString(style))
for _, r := range style {
src = append(src, []byte(string(r)))
}
copy(s.format, src)
}
func (s *barFiller) setReverse() {
s.reverse = true
}
func (s *barFiller) SetRefill(amount int64) {
s.refillAmount = amount
}
func (s *barFiller) Fill(w io.Writer, width int, stat *decor.Statistics) {
// don't count rLeft and rRight [brackets]
width -= 2
if width < 2 {
return
}
w.Write(s.format[rLeft])
if width == 2 {
w.Write(s.format[rRight])
return
}
bb := make([][]byte, width)
cwidth := int(internal.Percentage(stat.Total, stat.Current, int64(width)))
for i := 0; i < cwidth; i++ {
bb[i] = s.format[rFill]
}
if s.refillAmount > 0 {
var rwidth int
if s.refillAmount > stat.Current {
rwidth = cwidth
} else {
rwidth = int(internal.Percentage(stat.Total, int64(s.refillAmount), int64(width)))
}
for i := 0; i < rwidth; i++ {
bb[i] = s.format[rRefill]
}
}
if cwidth > 0 && cwidth < width {
bb[cwidth-1] = s.format[rTip]
}
for i := cwidth; i < width; i++ {
bb[i] = s.format[rEmpty]
}
if s.reverse {
if cwidth > 0 && cwidth < width {
bb[cwidth-1] = s.format[rRevTip]
}
for i := len(bb) - 1; i >= 0; i-- {
w.Write(bb[i])
}
} else {
for i := 0; i < len(bb); i++ {
w.Write(bb[i])
}
}
w.Write(s.format[rRight])
}

View File

@@ -6,11 +6,10 @@ import (
"github.com/vbauerster/mpb/decor"
)
// BarOption is a function option which changes the default behavior of a bar,
// if passed to p.AddBar(int64, ...BarOption)
// BarOption is a function option which changes the default behavior of a bar.
type BarOption func(*bState)
// AppendDecorators let you inject decorators to the bar's right side
// AppendDecorators let you inject decorators to the bar's right side.
func AppendDecorators(appenders ...decor.Decorator) BarOption {
return func(s *bState) {
for _, decorator := range appenders {
@@ -25,7 +24,7 @@ func AppendDecorators(appenders ...decor.Decorator) BarOption {
}
}
// PrependDecorators let you inject decorators to the bar's left side
// PrependDecorators let you inject decorators to the bar's left side.
func PrependDecorators(prependers ...decor.Decorator) BarOption {
return func(s *bState) {
for _, decorator := range prependers {
@@ -40,85 +39,155 @@ func PrependDecorators(prependers ...decor.Decorator) BarOption {
}
}
// BarTrimLeft trims left side space of the bar
func BarTrimLeft() BarOption {
return func(s *bState) {
s.trimLeftSpace = true
}
}
// BarTrimRight trims right space of the bar
func BarTrimRight() BarOption {
return func(s *bState) {
s.trimRightSpace = true
}
}
// BarTrim trims both left and right spaces of the bar
func BarTrim() BarOption {
return func(s *bState) {
s.trimLeftSpace = true
s.trimRightSpace = true
}
}
// BarID overwrites internal bar id
// BarID sets bar id.
func BarID(id int) BarOption {
return func(s *bState) {
s.id = id
}
}
// BarRemoveOnComplete is a flag, if set whole bar line will be removed on complete event.
// If both BarRemoveOnComplete and BarClearOnComplete are set, first bar section gets cleared
// and then whole bar line gets removed completely.
// BarWidth sets bar width independent of the container.
func BarWidth(width int) BarOption {
return func(s *bState) {
s.width = width
}
}
// BarRemoveOnComplete is a flag, if set whole bar line will be removed
// on complete event. If both BarRemoveOnComplete and BarClearOnComplete
// are set, first bar section gets cleared and then whole bar line
// gets removed completely.
func BarRemoveOnComplete() BarOption {
return func(s *bState) {
s.removeOnComplete = true
}
}
// BarReplaceOnComplete is indicator for delayed bar start, after the `runningBar` is complete.
// To achieve bar replacement effect, `runningBar` should has its `BarRemoveOnComplete` option set.
// BarReplaceOnComplete is indicator for delayed bar start, after the
// `runningBar` is complete. To achieve bar replacement effect,
// `runningBar` should has its `BarRemoveOnComplete` option set.
func BarReplaceOnComplete(runningBar *Bar) BarOption {
return BarParkTo(runningBar)
}
// BarParkTo same as BarReplaceOnComplete
func BarParkTo(runningBar *Bar) BarOption {
return func(s *bState) {
s.runningBar = runningBar
}
}
// BarClearOnComplete is a flag, if set will clear bar section on complete event.
// If you need to remove a whole bar line, refer to BarRemoveOnComplete.
// BarClearOnComplete is a flag, if set will clear bar section on
// complete event. If you need to remove a whole bar line, refer to
// BarRemoveOnComplete.
func BarClearOnComplete() BarOption {
return func(s *bState) {
s.barClearOnComplete = true
}
}
// BarPriority sets bar's priority.
// Zero is highest priority, i.e. bar will be on top.
// If `BarReplaceOnComplete` option is supplied, this option is ignored.
// BarPriority sets bar's priority. Zero is highest priority, i.e. bar
// will be on top. If `BarReplaceOnComplete` option is supplied, this
// option is ignored.
func BarPriority(priority int) BarOption {
return func(s *bState) {
s.priority = priority
}
}
// BarNewLineExtend takes user defined efn, which gets called each render cycle.
// Any write to provided writer of efn, will appear on new line of respective bar.
// BarNewLineExtend takes user defined efn, which gets called each
// render cycle. Any write to provided writer of efn, will appear on
// new line of respective bar.
func BarNewLineExtend(efn func(io.Writer, *decor.Statistics)) BarOption {
return func(s *bState) {
s.newLineExtendFn = efn
}
}
func barWidth(w int) BarOption {
// TrimSpace trims bar's edge spaces.
func TrimSpace() BarOption {
return func(s *bState) {
s.width = w
s.trimSpace = true
}
}
func barFormat(format string) BarOption {
// BarStyle sets custom bar style, default one is "[=>-]<+".
//
// '[' left bracket rune
//
// '=' fill rune
//
// '>' tip rune
//
// '-' empty rune
//
// ']' right bracket rune
//
// '<' reverse tip rune, used when BarReverse option is set
//
// '+' refill rune, used when *Bar.SetRefill(int64) is called
//
// It's ok to provide first five runes only, for example mpb.BarStyle("╢▌▌░╟")
func BarStyle(style string) BarOption {
chk := func(filler Filler) (interface{}, bool) {
if style == "" {
return nil, false
}
t, ok := filler.(*barFiller)
return t, ok
}
cb := func(t interface{}) {
t.(*barFiller).setStyle(style)
}
return MakeFillerTypeSpecificBarOption(chk, cb)
}
// BarReverse reverse mode, bar will progress from right to left.
func BarReverse() BarOption {
chk := func(filler Filler) (interface{}, bool) {
t, ok := filler.(*barFiller)
return t, ok
}
cb := func(t interface{}) {
t.(*barFiller).setReverse()
}
return MakeFillerTypeSpecificBarOption(chk, cb)
}
// SpinnerStyle sets custom spinner style.
// Effective when Filler type is spinner.
func SpinnerStyle(frames []string) BarOption {
chk := func(filler Filler) (interface{}, bool) {
if len(frames) == 0 {
return nil, false
}
t, ok := filler.(*spinnerFiller)
return t, ok
}
cb := func(t interface{}) {
t.(*spinnerFiller).frames = frames
}
return MakeFillerTypeSpecificBarOption(chk, cb)
}
// MakeFillerTypeSpecificBarOption makes BarOption specific to Filler's
// actual type. If you implement your own Filler, so most probably
// you'll need this. See BarStyle or SpinnerStyle for example.
func MakeFillerTypeSpecificBarOption(
typeChecker func(Filler) (interface{}, bool),
cb func(interface{}),
) BarOption {
return func(s *bState) {
s.runes = strToBarRunes(format)
if t, ok := typeChecker(s.filler); ok {
cb(t)
}
}
}
// OptionOnCondition returns option when condition evaluates to true.
func OptionOnCondition(option BarOption, condition func() bool) BarOption {
if condition() {
return option
}
return nil
}

View File

@@ -22,8 +22,8 @@ var (
clearCursorAndLine = cursorUp + clearLine
)
// Writer is a buffered the writer that updates the terminal.
// The contents of writer will be flushed when Flush is called.
// Writer is a buffered the writer that updates the terminal. The
// contents of writer will be flushed when Flush is called.
type Writer struct {
out io.Writer
buf bytes.Buffer
@@ -64,11 +64,13 @@ func (w *Writer) WriteString(s string) (n int, err error) {
return w.buf.WriteString(s)
}
// ReadFrom reads from the provided io.Reader and writes to the underlying buffer.
// ReadFrom reads from the provided io.Reader and writes to the
// underlying buffer.
func (w *Writer) ReadFrom(r io.Reader) (n int64, err error) {
return w.buf.ReadFrom(r)
}
// GetWidth returns width of underlying terminal.
func (w *Writer) GetWidth() (int, error) {
if w.isTerminal {
tw, _, err := terminal.GetSize(w.fd)

View File

@@ -8,7 +8,7 @@ import (
"syscall"
"unsafe"
"github.com/mattn/go-isatty"
isatty "github.com/mattn/go-isatty"
)
var kernel32 = syscall.NewLazyDLL("kernel32.dll")

View File

@@ -141,12 +141,14 @@ func CountersNoUnit(pairFormat string, wcc ...WC) Decorator {
return Counters(0, pairFormat, wcc...)
}
// CountersKibiByte is a wrapper around Counters with predefined unit UnitKiB (bytes/1024).
// CountersKibiByte is a wrapper around Counters with predefined unit
// UnitKiB (bytes/1024).
func CountersKibiByte(pairFormat string, wcc ...WC) Decorator {
return Counters(UnitKiB, pairFormat, wcc...)
}
// CountersKiloByte is a wrapper around Counters with predefined unit UnitKB (bytes/1000).
// CountersKiloByte is a wrapper around Counters with predefined unit
// UnitKB (bytes/1000).
func CountersKiloByte(pairFormat string, wcc ...WC) Decorator {
return Counters(UnitKB, pairFormat, wcc...)
}

View File

@@ -31,8 +31,12 @@ const (
DSyncSpaceR = DSyncWidth | DextraSpace | DidentRight
)
// TimeStyle enum.
type TimeStyle int
// TimeStyle kinds.
const (
ET_STYLE_GO = iota
ET_STYLE_GO TimeStyle = iota
ET_STYLE_HHMMSS
ET_STYLE_HHMM
ET_STYLE_MMSS
@@ -47,35 +51,37 @@ type Statistics struct {
}
// Decorator interface.
// A decorator must implement this interface, in order to be used with mpb library.
// A decorator must implement this interface, in order to be used with
// mpb library.
type Decorator interface {
Decor(*Statistics) string
Syncable
}
// Syncable interface.
// All decorators implement this interface implicitly.
// Its Syncable method exposes width sync channel, if sync is enabled.
// All decorators implement this interface implicitly. Its Syncable
// method exposes width sync channel, if sync is enabled.
type Syncable interface {
Syncable() (bool, chan int)
}
// OnCompleteMessenger interface.
// Decorators implementing this interface suppose to return provided string on complete event.
// Decorators implementing this interface suppose to return provided
// string on complete event.
type OnCompleteMessenger interface {
OnCompleteMessage(string)
}
// AmountReceiver interface.
// If decorator needs to receive increment amount,
// so this is the right interface to implement.
// If decorator needs to receive increment amount, so this is the right
// interface to implement.
type AmountReceiver interface {
NextAmount(int, ...time.Duration)
}
// ShutdownListener interface.
// If decorator needs to be notified once upon bar shutdown event,
// so this is the right interface to implement.
// If decorator needs to be notified once upon bar shutdown event, so
// this is the right interface to implement.
type ShutdownListener interface {
Shutdown()
}
@@ -90,6 +96,7 @@ var (
// WC is a struct with two public fields W and C, both of int type.
// W represents width and C represents bit set of width related config.
// A decorator should embed WC, in order to become Syncable.
type WC struct {
W int
C int
@@ -126,12 +133,13 @@ func (wc *WC) Init() {
}
}
// Syncable is implementation of Syncable interface.
func (wc *WC) Syncable() (bool, chan int) {
return (wc.C & DSyncWidth) != 0, wc.wsync
}
// OnComplete returns decorator, which wraps provided decorator, with sole
// purpose to display provided message on complete event.
// OnComplete returns decorator, which wraps provided decorator, with
// sole purpose to display provided message on complete event.
//
// `decorator` Decorator to wrap
//

View File

@@ -10,7 +10,7 @@ import (
// `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
//
// `wcc` optional WC config
func Elapsed(style int, wcc ...WC) Decorator {
func Elapsed(style TimeStyle, wcc ...WC) Decorator {
var wc WC
for _, widthConf := range wcc {
wc = widthConf
@@ -26,7 +26,7 @@ func Elapsed(style int, wcc ...WC) Decorator {
type elapsedDecorator struct {
WC
style int
style TimeStyle
startTime time.Time
msg string
completeMsg *string

View File

@@ -6,7 +6,6 @@ import (
"time"
"github.com/VividCortex/ewma"
"github.com/vbauerster/mpb/internal"
)
type TimeNormalizer func(time.Duration) time.Duration
@@ -18,7 +17,7 @@ type TimeNormalizer func(time.Duration) time.Duration
// `age` is the previous N samples to average over.
//
// `wcc` optional WC config
func EwmaETA(style int, age float64, wcc ...WC) Decorator {
func EwmaETA(style TimeStyle, age float64, wcc ...WC) Decorator {
return MovingAverageETA(style, ewma.NewMovingAverage(age), NopNormalizer(), wcc...)
}
@@ -31,7 +30,7 @@ func EwmaETA(style int, age float64, wcc ...WC) Decorator {
// `normalizer` available implementations are [NopNormalizer|FixedIntervalTimeNormalizer|MaxTolerateTimeNormalizer]
//
// `wcc` optional WC config
func MovingAverageETA(style int, average MovingAverage, normalizer TimeNormalizer, wcc ...WC) Decorator {
func MovingAverageETA(style TimeStyle, average MovingAverage, normalizer TimeNormalizer, wcc ...WC) Decorator {
var wc WC
for _, widthConf := range wcc {
wc = widthConf
@@ -48,7 +47,7 @@ func MovingAverageETA(style int, average MovingAverage, normalizer TimeNormalize
type movingAverageETA struct {
WC
style int
style TimeStyle
average ewma.MovingAverage
completeMsg *string
normalizer TimeNormalizer
@@ -59,7 +58,7 @@ func (d *movingAverageETA) Decor(st *Statistics) string {
return d.FormatMsg(*d.completeMsg)
}
v := internal.Round(d.average.Value())
v := math.Round(d.average.Value())
remaining := d.normalizer(time.Duration((st.Total - st.Current) * int64(v)))
hours := int64((remaining / time.Hour) % 60)
minutes := int64((remaining / time.Minute) % 60)
@@ -105,7 +104,7 @@ func (d *movingAverageETA) OnCompleteMessage(msg string) {
// `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
//
// `wcc` optional WC config
func AverageETA(style int, wcc ...WC) Decorator {
func AverageETA(style TimeStyle, wcc ...WC) Decorator {
var wc WC
for _, widthConf := range wcc {
wc = widthConf
@@ -121,7 +120,7 @@ func AverageETA(style int, wcc ...WC) Decorator {
type averageETA struct {
WC
style int
style TimeStyle
startTime time.Time
completeMsg *string
}
@@ -133,7 +132,7 @@ func (d *averageETA) Decor(st *Statistics) string {
var str string
timeElapsed := time.Since(d.startTime)
v := internal.Round(float64(timeElapsed) / float64(st.Current))
v := math.Round(float64(timeElapsed) / float64(st.Current))
if math.IsInf(v, 0) || math.IsNaN(v) {
v = 0
}

View File

@@ -6,9 +6,9 @@ import (
"github.com/VividCortex/ewma"
)
// MovingAverage is the interface that computes a moving average over a time-
// series stream of numbers. The average may be over a window or exponentially
// decaying.
// MovingAverage is the interface that computes a moving average over
// a time-series stream of numbers. The average may be over a window
// or exponentially decaying.
type MovingAverage interface {
Add(float64)
Value() float64
@@ -57,7 +57,8 @@ func (s *medianEwma) Add(v float64) {
s.count++
}
// NewMedianEwma is ewma based MovingAverage, which gets its values from median MovingAverage.
// NewMedianEwma is ewma based MovingAverage, which gets its values
// from median MovingAverage.
func NewMedianEwma(age ...float64) MovingAverage {
return &medianEwma{
MovingAverage: ewma.NewMovingAverage(age...),

View File

@@ -137,7 +137,8 @@ func EwmaSpeed(unit int, unitFormat string, age float64, wcc ...WC) Decorator {
return MovingAverageSpeed(unit, unitFormat, ewma.NewMovingAverage(age), wcc...)
}
// MovingAverageSpeed decorator relies on MovingAverage implementation to calculate its average.
// MovingAverageSpeed decorator relies on MovingAverage implementation
// to calculate its average.
//
// `unit` one of [0|UnitKiB|UnitKB] zero for no unit
//

View File

@@ -1,10 +1,12 @@
package internal
import "math"
// Percentage is a helper function, to calculate percentage.
func Percentage(total, current, width int64) int64 {
if total <= 0 {
return 0
}
p := float64(width*current) / float64(total)
return int64(Round(p))
return int64(math.Round(p))
}

View File

@@ -1,49 +0,0 @@
package internal
import "math"
const (
uvone = 0x3FF0000000000000
mask = 0x7FF
shift = 64 - 11 - 1
bias = 1023
signMask = 1 << 63
fracMask = 1<<shift - 1
)
// Round returns the nearest integer, rounding half away from zero.
//
// Special cases are:
// Round(±0) = ±0
// Round(±Inf) = ±Inf
// Round(NaN) = NaN
func Round(x float64) float64 {
// Round is a faster implementation of:
//
// func Round(x float64) float64 {
// t := Trunc(x)
// if Abs(x-t) >= 0.5 {
// return t + Copysign(1, x)
// }
// return t
// }
bits := math.Float64bits(x)
e := uint(bits>>shift) & mask
if e < bias {
// Round abs(x) < 1 including denormals.
bits &= signMask // +-0
if e == bias-1 {
bits |= uvone // +-1
}
} else if e < bias+shift {
// Round any abs(x) >= 1 containing a fractional component [0,1).
//
// Numbers with larger exponents are returned unchanged since they
// must be either an integer, infinity, or NaN.
const half = 1 << (shift - 1)
e -= bias
bits += half >> e
bits &^= fracMask >> e
}
return math.Float64frombits(bits)
}

View File

@@ -1,29 +1,30 @@
package mpb
import (
"context"
"io"
"sync"
"time"
"unicode/utf8"
"github.com/vbauerster/mpb/cwriter"
)
// ProgressOption is a function option which changes the default behavior of
// progress pool, if passed to mpb.New(...ProgressOption)
// ProgressOption is a function option which changes the default
// behavior of progress pool, if passed to mpb.New(...ProgressOption).
type ProgressOption func(*pState)
// WithWaitGroup provides means to have a single joint point.
// If *sync.WaitGroup is provided, you can safely call just p.Wait()
// without calling Wait() on provided *sync.WaitGroup.
// Makes sense when there are more than one bar to render.
// WithWaitGroup provides means to have a single joint point. If
// *sync.WaitGroup is provided, you can safely call just p.Wait()
// without calling Wait() on provided *sync.WaitGroup. Makes sense
// when there are more than one bar to render.
func WithWaitGroup(wg *sync.WaitGroup) ProgressOption {
return func(s *pState) {
s.uwg = wg
}
}
// WithWidth overrides default width 80
// WithWidth sets container width. Default is 80. Bars inherit this
// width, as long as no BarWidth is applied.
func WithWidth(w int) ProgressOption {
return func(s *pState) {
if w >= 0 {
@@ -32,16 +33,7 @@ func WithWidth(w int) ProgressOption {
}
}
// WithFormat overrides default bar format "[=>-]"
func WithFormat(format string) ProgressOption {
return func(s *pState) {
if utf8.RuneCountInString(format) == formatLen {
s.format = format
}
}
}
// WithRefreshRate overrides default 120ms refresh rate
// WithRefreshRate overrides default 120ms refresh rate.
func WithRefreshRate(d time.Duration) ProgressOption {
return func(s *pState) {
if d < 10*time.Millisecond {
@@ -59,22 +51,25 @@ func WithManualRefresh(ch <-chan time.Time) ProgressOption {
}
}
// WithCancel provide your cancel channel,
// which you plan to close at some point.
func WithCancel(ch <-chan struct{}) ProgressOption {
// WithContext provided context will be used for cancellation purposes.
func WithContext(ctx context.Context) ProgressOption {
return func(s *pState) {
s.cancel = ch
if ctx == nil {
return
}
s.ctx = ctx
}
}
// WithShutdownNotifier provided chanel will be closed, after all bars have been rendered.
// WithShutdownNotifier provided chanel will be closed, after all bars
// have been rendered.
func WithShutdownNotifier(ch chan struct{}) ProgressOption {
return func(s *pState) {
s.shutdownNotifier = ch
}
}
// WithOutput overrides default output os.Stdout
// WithOutput overrides default output os.Stdout.
func WithOutput(w io.Writer) ProgressOption {
return func(s *pState) {
if w == nil {

View File

@@ -1,15 +0,0 @@
//+build go1.7
package mpb
import "context"
// WithContext provided context will be used for cancellation purposes
func WithContext(ctx context.Context) ProgressOption {
return func(s *pState) {
if ctx == nil {
panic("ctx must not be nil")
}
s.cancel = ctx.Done()
}
}

View File

@@ -2,6 +2,7 @@ package mpb
import (
"container/heap"
"context"
"fmt"
"io"
"io/ioutil"
@@ -17,8 +18,6 @@ const (
prr = 120 * time.Millisecond
// default width
pwidth = 80
// default format
pformat = "[=>-]"
)
// Progress represents the container that renders Progress bars
@@ -42,24 +41,24 @@ type pState struct {
pMatrix map[int][]chan int
aMatrix map[int][]chan int
// following are provided by user
// following are provided/overrided by user
ctx context.Context
uwg *sync.WaitGroup
manualRefreshCh <-chan time.Time
cancel <-chan struct{}
shutdownNotifier chan struct{}
waitBars map[*Bar]*Bar
debugOut io.Writer
}
// New creates new Progress instance, which orchestrates bars rendering process.
// Accepts mpb.ProgressOption funcs for customization.
// New creates new Progress instance, which orchestrates bars rendering
// process. Accepts mpb.ProgressOption funcs for customization.
func New(options ...ProgressOption) *Progress {
pq := make(priorityQueue, 0)
heap.Init(&pq)
s := &pState{
ctx: context.Background(),
bHeap: &pq,
width: pwidth,
format: pformat,
cw: cwriter.New(os.Stdout),
rr: prr,
waitBars: make(map[*Bar]*Bar),
@@ -84,12 +83,28 @@ func New(options ...ProgressOption) *Progress {
// AddBar creates a new progress bar and adds to the container.
func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
return p.Add(total, newDefaultBarFiller(), options...)
}
// AddSpinner creates a new spinner bar and adds to the container.
func (p *Progress) AddSpinner(total int64, alignment SpinnerAlignment, options ...BarOption) *Bar {
filler := &spinnerFiller{
frames: defaultSpinnerStyle,
alignment: alignment,
}
return p.Add(total, filler, options...)
}
// Add creates a bar which renders itself by provided filler.
func (p *Progress) Add(total int64, filler Filler, options ...BarOption) *Bar {
if filler == nil {
filler = newDefaultBarFiller()
}
p.wg.Add(1)
result := make(chan *Bar)
select {
case p.operateState <- func(s *pState) {
options = append(options, barWidth(s.width), barFormat(s.format))
b := newBar(p.wg, s.idCounter, total, s.cancel, options...)
b := newBar(s.ctx, p.wg, filler, s.idCounter, s.width, total, options...)
if b.runningBar != nil {
s.waitBars[b.runningBar] = b
} else {
@@ -106,10 +121,10 @@ func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
}
}
// Abort is only effective while bar progress is running,
// it means remove bar now without waiting for its completion.
// If bar is already completed, there is nothing to abort.
// If you need to remove bar after completion, use BarRemoveOnComplete BarOption.
// Abort is only effective while bar progress is running, it means
// remove bar now without waiting for its completion. If bar is already
// completed, there is nothing to abort. If you need to remove bar
// after completion, use BarRemoveOnComplete BarOption.
func (p *Progress) Abort(b *Bar, remove bool) {
select {
case p.operateState <- func(s *pState) {
@@ -145,9 +160,10 @@ func (p *Progress) BarCount() int {
}
}
// Wait first waits for user provided *sync.WaitGroup, if any,
// then waits far all bars to complete and finally shutdowns master goroutine.
// After this method has been called, there is no way to reuse *Progress instance.
// Wait first waits for user provided *sync.WaitGroup, if any, then
// waits far all bars to complete and finally shutdowns master goroutine.
// After this method has been called, there is no way to reuse *Progress
// instance.
func (p *Progress) Wait() {
if p.uwg != nil {
p.uwg.Wait()
@@ -205,8 +221,8 @@ func (s *pState) flush(lineCount int) error {
defer func() {
if frameReader.toShutdown {
// shutdown at next flush, in other words decrement underlying WaitGroup
// only after the bar with completed state has been flushed.
// this ensures no bar ends up with less than 100% rendered.
// only after the bar with completed state has been flushed. this
// ensures no bar ends up with less than 100% rendered.
s.shutdownPending = append(s.shutdownPending, bar)
if replacementBar, ok := s.waitBars[bar]; ok {
heap.Push(s.bHeap, replacementBar)

48
vendor/github.com/vbauerster/mpb/spinner_filler.go generated vendored Normal file
View File

@@ -0,0 +1,48 @@
package mpb
import (
"io"
"strings"
"unicode/utf8"
"github.com/vbauerster/mpb/decor"
)
// SpinnerAlignment enum.
type SpinnerAlignment int
// SpinnerAlignment kinds.
const (
SpinnerOnLeft SpinnerAlignment = iota
SpinnerOnMiddle
SpinnerOnRight
)
var defaultSpinnerStyle = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
type spinnerFiller struct {
frames []string
count uint
alignment SpinnerAlignment
}
func (s *spinnerFiller) Fill(w io.Writer, width int, stat *decor.Statistics) {
frame := s.frames[s.count%uint(len(s.frames))]
frameWidth := utf8.RuneCountInString(frame)
if width < frameWidth {
return
}
switch rest := width - frameWidth; s.alignment {
case SpinnerOnLeft:
io.WriteString(w, frame+strings.Repeat(" ", rest))
case SpinnerOnMiddle:
str := strings.Repeat(" ", rest/2) + frame + strings.Repeat(" ", rest/2+rest%2)
io.WriteString(w, str)
case SpinnerOnRight:
io.WriteString(w, strings.Repeat(" ", rest)+frame)
}
s.count++
}