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

4
go.mod
View File

@ -9,7 +9,7 @@ require (
github.com/VividCortex/ewma v1.1.1 // indirect github.com/VividCortex/ewma v1.1.1 // indirect
github.com/containerd/continuity v0.0.0-20180216233310-d8fb8589b0e8 // indirect github.com/containerd/continuity v0.0.0-20180216233310-d8fb8589b0e8 // indirect
github.com/containers/buildah v1.8.4 github.com/containers/buildah v1.8.4
github.com/containers/image v1.5.2-0.20190620105408-93b1deece293 github.com/containers/image v1.5.2-0.20190717062552-2178abd5f9b1
github.com/containers/storage v1.12.10 github.com/containers/storage v1.12.10
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/distribution v0.0.0-20170817175659-5f6282db7d65 // indirect github.com/docker/distribution v0.0.0-20170817175659-5f6282db7d65 // indirect
@ -51,7 +51,7 @@ require (
github.com/ulikunitz/xz v0.5.4 // indirect github.com/ulikunitz/xz v0.5.4 // indirect
github.com/urfave/cli v1.20.0 github.com/urfave/cli v1.20.0
github.com/vbatts/tar-split v0.10.2 // indirect github.com/vbatts/tar-split v0.10.2 // indirect
github.com/vbauerster/mpb v3.3.4+incompatible // indirect github.com/vbauerster/mpb v3.4.0+incompatible // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.1.0 // indirect github.com/xeipuuv/gojsonschema v1.1.0 // indirect

6
go.sum
View File

@ -12,6 +12,8 @@ github.com/containers/buildah v1.8.4 h1:06c+UNeEWMa2wA1Z7muZ0ZqUzE91sDuZJbB0BiZa
github.com/containers/buildah v1.8.4/go.mod h1:1CsiLJvyU+h+wOjnqJJOWuJCVcMxZOr5HN/gHGdzJxY= github.com/containers/buildah v1.8.4/go.mod h1:1CsiLJvyU+h+wOjnqJJOWuJCVcMxZOr5HN/gHGdzJxY=
github.com/containers/image v1.5.2-0.20190620105408-93b1deece293 h1:EalCgZ875kDCN2HcOch50q48GKerWGc5eV0BllCvln8= github.com/containers/image v1.5.2-0.20190620105408-93b1deece293 h1:EalCgZ875kDCN2HcOch50q48GKerWGc5eV0BllCvln8=
github.com/containers/image v1.5.2-0.20190620105408-93b1deece293/go.mod h1:8Vtij257IWSanUQKe1tAeNOm2sRVkSqQTVQ1IlwI3+M= github.com/containers/image v1.5.2-0.20190620105408-93b1deece293/go.mod h1:8Vtij257IWSanUQKe1tAeNOm2sRVkSqQTVQ1IlwI3+M=
github.com/containers/image v1.5.2-0.20190717062552-2178abd5f9b1 h1:RGlzwWSoGBbc5fgGysRrGAPLn8xQwihzRVPVDW5yQlo=
github.com/containers/image v1.5.2-0.20190717062552-2178abd5f9b1/go.mod h1:8Vtij257IWSanUQKe1tAeNOm2sRVkSqQTVQ1IlwI3+M=
github.com/containers/image v2.0.0+incompatible h1:FTr6Br7jlIKNCKMjSOMbAxKp2keQ0//jzJaYNTVhauk= github.com/containers/image v2.0.0+incompatible h1:FTr6Br7jlIKNCKMjSOMbAxKp2keQ0//jzJaYNTVhauk=
github.com/containers/image v2.0.0+incompatible/go.mod h1:8Vtij257IWSanUQKe1tAeNOm2sRVkSqQTVQ1IlwI3+M= github.com/containers/image v2.0.0+incompatible/go.mod h1:8Vtij257IWSanUQKe1tAeNOm2sRVkSqQTVQ1IlwI3+M=
github.com/containers/storage v1.12.10 h1:vw1aiLsZ1LvO09ELMxVBTe35tThRiMftI2cPeH+G5ow= github.com/containers/storage v1.12.10 h1:vw1aiLsZ1LvO09ELMxVBTe35tThRiMftI2cPeH+G5ow=
@ -101,6 +103,10 @@ github.com/vbatts/tar-split v0.10.2 h1:CXd7HEKGkTLjBMinpObcJZU5Hm8EKlor2a1JtX6ms
github.com/vbatts/tar-split v0.10.2/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpcEPSzR8z6g= github.com/vbatts/tar-split v0.10.2/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpcEPSzR8z6g=
github.com/vbauerster/mpb v3.3.4+incompatible h1:DDIhnwmgTQIDZo+SWlEr5d6mJBxkOLBwCXPzunhEfJ4= github.com/vbauerster/mpb v3.3.4+incompatible h1:DDIhnwmgTQIDZo+SWlEr5d6mJBxkOLBwCXPzunhEfJ4=
github.com/vbauerster/mpb v3.3.4+incompatible/go.mod h1:zAHG26FUhVKETRu+MWqYXcI70POlC6N8up9p1dID7SU= github.com/vbauerster/mpb v3.3.4+incompatible/go.mod h1:zAHG26FUhVKETRu+MWqYXcI70POlC6N8up9p1dID7SU=
github.com/vbauerster/mpb v3.4.0+incompatible h1:mfiiYw87ARaeRW6x5gWwYRUawxaW1tLAD8IceomUCNw=
github.com/vbauerster/mpb v3.4.0+incompatible/go.mod h1:zAHG26FUhVKETRu+MWqYXcI70POlC6N8up9p1dID7SU=
github.com/vrothberg/image v0.0.0-20190717060034-cd5ce8239f51 h1:u4Hw4D3PLODtsZJ1FKi7j8bkd+zyJOc28dRSiVTOgyE=
github.com/vrothberg/image v0.0.0-20190717060034-cd5ce8239f51/go.mod h1:/hIyjuUvIY6X2wGj/fbsA9zwlfAize8B2DLPishEHHg=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=

View File

@ -597,15 +597,32 @@ func (c *copier) createProgressBar(pool *mpb.Progress, info types.BlobInfo, kind
prefix = prefix[:maxPrefixLen] prefix = prefix[:maxPrefixLen]
} }
bar := pool.AddBar(info.Size, // Use a normal progress bar when we know the size (i.e., size > 0).
mpb.BarClearOnComplete(), // Otherwise, use a spinner to indicate that something's happening.
mpb.PrependDecorators( var bar *mpb.Bar
decor.Name(prefix), if info.Size > 0 {
), bar = pool.AddBar(info.Size,
mpb.AppendDecorators( mpb.BarClearOnComplete(),
decor.OnComplete(decor.CountersKibiByte("%.1f / %.1f"), " "+onComplete), mpb.PrependDecorators(
), decor.Name(prefix),
) ),
mpb.AppendDecorators(
decor.OnComplete(decor.CountersKibiByte("%.1f / %.1f"), " "+onComplete),
),
)
} else {
bar = pool.AddSpinner(info.Size,
mpb.SpinnerOnLeft,
mpb.BarClearOnComplete(),
mpb.SpinnerStyle([]string{".", "..", "...", "....", ""}),
mpb.PrependDecorators(
decor.Name(prefix),
),
mpb.AppendDecorators(
decor.OnComplete(decor.Name(""), " "+onComplete),
),
)
}
if c.progressOutput == ioutil.Discard { if c.progressOutput == ioutil.Discard {
c.Printf("Copying %s %s\n", kind, info.Digest) c.Printf("Copying %s %s\n", kind, info.Digest)
} }

View File

@ -226,6 +226,7 @@ func (m *Schema1) Inspect(_ func(types.BlobInfo) ([]byte, error)) (*types.ImageI
} }
if s1.Config != nil { if s1.Config != nil {
i.Labels = s1.Config.Labels i.Labels = s1.Config.Labels
i.Env = s1.Config.Env
} }
return i, nil return i, nil
} }

View File

@ -241,6 +241,7 @@ func (m *Schema2) Inspect(configGetter func(types.BlobInfo) ([]byte, error)) (*t
} }
if s2.Config != nil { if s2.Config != nil {
i.Labels = s2.Config.Labels i.Labels = s2.Config.Labels
i.Env = s2.Config.Env
} }
return i, nil return i, nil
} }

View File

@ -116,6 +116,7 @@ func (m *OCI1) Inspect(configGetter func(types.BlobInfo) ([]byte, error)) (*type
Architecture: v1.Architecture, Architecture: v1.Architecture,
Os: v1.OS, Os: v1.OS,
Layers: layerInfosToStrings(m.LayerInfos()), Layers: layerInfosToStrings(m.LayerInfos()),
Env: d1.Config.Env,
} }
return i, nil return i, nil
} }

View File

@ -1,4 +1,4 @@
// +build !containers_image_ostree_stub // +build containers_image_ostree
package ostree package ostree

View File

@ -1,4 +1,4 @@
// +build !containers_image_ostree_stub // +build containers_image_ostree
package ostree package ostree

View File

@ -1,4 +1,4 @@
// +build !containers_image_ostree_stub // +build containers_image_ostree
package ostree package ostree

View File

@ -56,6 +56,7 @@ func SetAuthentication(sys *types.SystemContext, registry, username, password st
// If an entry is not found empty strings are returned for the username and password // If an entry is not found empty strings are returned for the username and password
func GetAuthentication(sys *types.SystemContext, registry string) (string, string, error) { func GetAuthentication(sys *types.SystemContext, registry string) (string, string, error) {
if sys != nil && sys.DockerAuthConfig != nil { if sys != nil && sys.DockerAuthConfig != nil {
logrus.Debug("Returning credentials from DockerAuthConfig")
return sys.DockerAuthConfig.Username, sys.DockerAuthConfig.Password, nil return sys.DockerAuthConfig.Username, sys.DockerAuthConfig.Password, nil
} }
@ -76,12 +77,15 @@ func GetAuthentication(sys *types.SystemContext, registry string) (string, strin
legacyFormat := path == dockerLegacyPath legacyFormat := path == dockerLegacyPath
username, password, err := findAuthentication(registry, path, legacyFormat) username, password, err := findAuthentication(registry, path, legacyFormat)
if err != nil { if err != nil {
logrus.Debugf("Credentials not found")
return "", "", err return "", "", err
} }
if username != "" && password != "" { if username != "" && password != "" {
logrus.Debugf("Returning credentials from %s", path)
return username, password, nil return username, password, nil
} }
} }
logrus.Debugf("Credentials not found")
return "", "", nil return "", "", nil
} }

View File

@ -1,4 +1,4 @@
// +build !containers_image_ostree_stub,linux // +build containers_image_ostree,linux
package alltransports package alltransports

View File

@ -1,4 +1,4 @@
// +build containers_image_ostree_stub !linux // +build !containers_image_ostree !linux
package alltransports package alltransports

View File

@ -398,6 +398,7 @@ type ImageInspectInfo struct {
Architecture string Architecture string
Os string Os string
Layers []string Layers []string
Env []string
} }
// DockerAuthConfig contains authorization information for connecting to a registry. // DockerAuthConfig contains authorization information for connecting to a registry.

View File

@ -1,8 +1,8 @@
language: go language: go
sudo: false sudo: false
go: go:
- 1.8.x - 1.10.x
- 1.9.x - tip
before_install: before_install:
- go get -t -v ./... - 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( p := mpb.New(
// override default (80) width // override default (80) width
mpb.WithWidth(64), mpb.WithWidth(64),
// override default "[=>-]" format
mpb.WithFormat("╢▌▌░╟"),
// override default 120ms refresh rate // override default 120ms refresh rate
mpb.WithRefreshRate(180*time.Millisecond), 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:" name := "Single Bar:"
// adding a single bar // adding a single bar
bar := p.AddBar(int64(total), bar := p.AddBar(int64(total),
// override default "[=>-]" style
mpb.BarStyle("╢▌▌░╟"),
mpb.PrependDecorators( mpb.PrependDecorators(
// display our name with one space on the right // display our name with one space on the right
decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}), decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}),

View File

@ -2,6 +2,7 @@ package mpb
import ( import (
"bytes" "bytes"
"context"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
@ -11,21 +12,8 @@ import (
"unicode/utf8" "unicode/utf8"
"github.com/vbauerster/mpb/decor" "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 // Bar represents a progress Bar
type Bar struct { type Bar struct {
priority int priority int
@ -45,15 +33,30 @@ type Bar struct {
shutdown chan 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 ( type (
bState struct { bState struct {
filler Filler
id int id int
width int width int
alignment int
total int64 total int64
current int64 current int64
runes barRunes trimSpace bool
trimLeftSpace bool
trimRightSpace bool
toComplete bool toComplete bool
removeOnComplete bool removeOnComplete bool
barClearOnComplete bool barClearOnComplete bool
@ -73,8 +76,8 @@ type (
runningBar *Bar runningBar *Bar
} }
refill struct { refill struct {
char rune r rune
till int64 limit int64
} }
frameReader struct { frameReader struct {
io.Reader io.Reader
@ -84,14 +87,20 @@ type (
} }
) )
func newBar(wg *sync.WaitGroup, id int, total int64, cancel <-chan struct{}, options ...BarOption) *Bar { func newBar(
if total <= 0 { ctx context.Context,
total = time.Now().Unix() wg *sync.WaitGroup,
} filler Filler,
id, width int,
total int64,
options ...BarOption,
) *Bar {
s := &bState{ s := &bState{
filler: filler,
id: id, id: id,
priority: id, priority: id,
width: width,
total: total, 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.bufP = bytes.NewBuffer(make([]byte, 0, s.width))
s.bufB = 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)) s.bufA = bytes.NewBuffer(make([]byte, 0, s.width))
if s.newLineExtendFn != nil {
s.bufNL = bytes.NewBuffer(make([]byte, 0, s.width))
}
b := &Bar{ b := &Bar{
priority: s.priority, 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 b.priority = b.runningBar.priority
} }
if s.newLineExtendFn != nil { go b.serve(ctx, wg, s)
s.bufNL = bytes.NewBuffer(make([]byte, 0, s.width))
}
go b.serve(wg, s, cancel)
return b return b
} }
@ -178,52 +186,42 @@ func (b *Bar) Current() int64 {
} }
// SetTotal sets total dynamically. // SetTotal sets total dynamically.
// Set final to true, when total is known, it will trigger bar complete event. // Set complete to true, to trigger bar complete event now.
func (b *Bar) SetTotal(total int64, final bool) bool { func (b *Bar) SetTotal(total int64, complete bool) {
select { select {
case b.operateState <- func(s *bState) { case b.operateState <- func(s *bState) {
if total > 0 { s.total = total
s.total = total if complete && !s.toComplete {
}
if final {
s.current = s.total s.current = s.total
s.toComplete = true s.toComplete = true
} }
}: }:
return true
case <-b.done: case <-b.done:
return false
} }
} }
// SetRefill sets fill rune to r, up until n. // SetRefill sets refill, if supported by underlying Filler.
func (b *Bar) SetRefill(n int, r rune) { func (b *Bar) SetRefill(amount int64) {
if n <= 0 {
return
}
b.operateState <- func(s *bState) { 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). // Increment is a shorthand for b.IncrBy(1).
func (b *Bar) Increment() { func (b *Bar) Increment() {
b.IncrBy(1) b.IncrBy(1)
} }
// IncrBy increments progress bar by amount of n. // IncrBy increments progress bar by amount of n.
// wdd is optional work duration i.e. time.Since(start), // wdd is optional work duration i.e. time.Since(start), which expected
// which expected to be provided, if any ewma based decorator is used. // to be provided, if any ewma based decorator is used.
func (b *Bar) IncrBy(n int, wdd ...time.Duration) { func (b *Bar) IncrBy(n int, wdd ...time.Duration) {
select { select {
case b.operateState <- func(s *bState) { case b.operateState <- func(s *bState) {
s.current += int64(n) s.current += int64(n)
if s.current >= s.total { if s.total > 0 && s.current >= s.total {
s.current = s.total s.current = s.total
s.toComplete = true 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. // Completed reports whether the bar is in completed state.
func (b *Bar) Completed() bool { func (b *Bar) Completed() bool {
// omit select here, because primary usage of the method is for loop // omit select here, because primary usage of the method is for loop
// condition, like for !bar.Completed() {...} // condition, like for !bar.Completed() {...} so when toComplete=true
// so when toComplete=true it is called once (at which time, the bar is still alive), // it is called once (at which time, the bar is still alive), then
// then quits the loop and never suppose to be called afterwards. // quits the loop and never suppose to be called afterwards.
return <-b.boolCh 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() defer wg.Done()
cancel := ctx.Done()
for { for {
select { select {
case op := <-b.operateState: 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 { func (s *bState) draw(termWidth int) io.Reader {
defer s.bufA.WriteByte('\n')
if s.panicMsg != "" { if s.panicMsg != "" {
return strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", termWidth), 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)) s.bufA.WriteString(d.Decor(stat))
} }
prependCount := utf8.RuneCount(s.bufP.Bytes())
appendCount := utf8.RuneCount(s.bufA.Bytes())
if s.barClearOnComplete && s.completeFlushed { if s.barClearOnComplete && s.completeFlushed {
s.bufA.WriteByte('\n')
return io.MultiReader(s.bufP, s.bufA) return io.MultiReader(s.bufP, s.bufA)
} }
s.fillBar(s.width) prependCount := utf8.RuneCount(s.bufP.Bytes())
barCount := utf8.RuneCount(s.bufB.Bytes()) appendCount := utf8.RuneCount(s.bufA.Bytes())
totalCount := prependCount + barCount + appendCount
if spaceCount := 0; totalCount > termWidth {
if !s.trimLeftSpace {
spaceCount++
}
if !s.trimRightSpace {
spaceCount++
}
s.fillBar(termWidth - prependCount - appendCount - spaceCount)
}
return io.MultiReader(s.bufP, s.bufB, s.bufA) if !s.trimSpace {
} // reserve space for edge spaces
termWidth -= 2
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 {
s.bufB.WriteByte(' ') s.bufB.WriteByte(' ')
} }
s.bufB.WriteRune(s.runes[rLeft])
if width <= 2 {
return
}
// bar s.width without leftEnd and rightEnd runes if prependCount+s.width+appendCount > termWidth {
barWidth := width - 2 s.filler.Fill(s.bufB, termWidth-prependCount-appendCount, stat)
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])
}
} else { } else {
var i int64 s.filler.Fill(s.bufB, s.width, stat)
for i = 0; i < completedWidth; i++ {
s.bufB.WriteRune(s.runes[rFill])
}
} }
if completedWidth < int64(barWidth) && completedWidth > 0 { if !s.trimSpace {
_, size := utf8.DecodeLastRune(s.bufB.Bytes()) s.bufB.WriteByte(' ')
s.bufB.Truncate(s.bufB.Len() - size)
s.bufB.WriteRune(s.runes[rTip])
} }
for i := completedWidth; i < int64(barWidth); i++ { s.bufA.WriteByte('\n')
s.bufB.WriteRune(s.runes[rEmpty]) return io.MultiReader(s.bufP, s.bufB, s.bufA)
}
} }
func (s *bState) wSyncTable() [][]chan int { 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 { func countLines(b []byte) int {
return bytes.Count(b, []byte("\n")) 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" "github.com/vbauerster/mpb/decor"
) )
// BarOption is a function option which changes the default behavior of a bar, // BarOption is a function option which changes the default behavior of a bar.
// if passed to p.AddBar(int64, ...BarOption)
type BarOption func(*bState) 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 { func AppendDecorators(appenders ...decor.Decorator) BarOption {
return func(s *bState) { return func(s *bState) {
for _, decorator := range appenders { 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 { func PrependDecorators(prependers ...decor.Decorator) BarOption {
return func(s *bState) { return func(s *bState) {
for _, decorator := range prependers { for _, decorator := range prependers {
@ -40,85 +39,155 @@ func PrependDecorators(prependers ...decor.Decorator) BarOption {
} }
} }
// BarTrimLeft trims left side space of the bar // BarID sets bar id.
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
func BarID(id int) BarOption { func BarID(id int) BarOption {
return func(s *bState) { return func(s *bState) {
s.id = id s.id = id
} }
} }
// BarRemoveOnComplete is a flag, if set whole bar line will be removed on complete event. // BarWidth sets bar width independent of the container.
// If both BarRemoveOnComplete and BarClearOnComplete are set, first bar section gets cleared func BarWidth(width int) BarOption {
// and then whole bar line gets removed completely. 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 { func BarRemoveOnComplete() BarOption {
return func(s *bState) { return func(s *bState) {
s.removeOnComplete = true s.removeOnComplete = true
} }
} }
// BarReplaceOnComplete is indicator for delayed bar start, after the `runningBar` is complete. // BarReplaceOnComplete is indicator for delayed bar start, after the
// To achieve bar replacement effect, `runningBar` should has its `BarRemoveOnComplete` option set. // `runningBar` is complete. To achieve bar replacement effect,
// `runningBar` should has its `BarRemoveOnComplete` option set.
func BarReplaceOnComplete(runningBar *Bar) BarOption { func BarReplaceOnComplete(runningBar *Bar) BarOption {
return BarParkTo(runningBar)
}
// BarParkTo same as BarReplaceOnComplete
func BarParkTo(runningBar *Bar) BarOption {
return func(s *bState) { return func(s *bState) {
s.runningBar = runningBar s.runningBar = runningBar
} }
} }
// BarClearOnComplete is a flag, if set will clear bar section on complete event. // BarClearOnComplete is a flag, if set will clear bar section on
// If you need to remove a whole bar line, refer to BarRemoveOnComplete. // complete event. If you need to remove a whole bar line, refer to
// BarRemoveOnComplete.
func BarClearOnComplete() BarOption { func BarClearOnComplete() BarOption {
return func(s *bState) { return func(s *bState) {
s.barClearOnComplete = true s.barClearOnComplete = true
} }
} }
// BarPriority sets bar's priority. // BarPriority sets bar's priority. Zero is highest priority, i.e. bar
// Zero is highest priority, i.e. bar will be on top. // will be on top. If `BarReplaceOnComplete` option is supplied, this
// If `BarReplaceOnComplete` option is supplied, this option is ignored. // option is ignored.
func BarPriority(priority int) BarOption { func BarPriority(priority int) BarOption {
return func(s *bState) { return func(s *bState) {
s.priority = priority s.priority = priority
} }
} }
// BarNewLineExtend takes user defined efn, which gets called each render cycle. // BarNewLineExtend takes user defined efn, which gets called each
// Any write to provided writer of efn, will appear on new line of respective bar. // 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 { func BarNewLineExtend(efn func(io.Writer, *decor.Statistics)) BarOption {
return func(s *bState) { return func(s *bState) {
s.newLineExtendFn = efn s.newLineExtendFn = efn
} }
} }
func barWidth(w int) BarOption { // TrimSpace trims bar's edge spaces.
func TrimSpace() BarOption {
return func(s *bState) { 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) { 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 clearCursorAndLine = cursorUp + clearLine
) )
// Writer is a buffered the writer that updates the terminal. // Writer is a buffered the writer that updates the terminal. The
// The contents of writer will be flushed when Flush is called. // contents of writer will be flushed when Flush is called.
type Writer struct { type Writer struct {
out io.Writer out io.Writer
buf bytes.Buffer buf bytes.Buffer
@ -64,11 +64,13 @@ func (w *Writer) WriteString(s string) (n int, err error) {
return w.buf.WriteString(s) 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) { func (w *Writer) ReadFrom(r io.Reader) (n int64, err error) {
return w.buf.ReadFrom(r) return w.buf.ReadFrom(r)
} }
// GetWidth returns width of underlying terminal.
func (w *Writer) GetWidth() (int, error) { func (w *Writer) GetWidth() (int, error) {
if w.isTerminal { if w.isTerminal {
tw, _, err := terminal.GetSize(w.fd) tw, _, err := terminal.GetSize(w.fd)

View File

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

View File

@ -141,12 +141,14 @@ func CountersNoUnit(pairFormat string, wcc ...WC) Decorator {
return Counters(0, pairFormat, wcc...) 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 { func CountersKibiByte(pairFormat string, wcc ...WC) Decorator {
return Counters(UnitKiB, pairFormat, wcc...) 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 { func CountersKiloByte(pairFormat string, wcc ...WC) Decorator {
return Counters(UnitKB, pairFormat, wcc...) return Counters(UnitKB, pairFormat, wcc...)
} }

View File

@ -31,8 +31,12 @@ const (
DSyncSpaceR = DSyncWidth | DextraSpace | DidentRight DSyncSpaceR = DSyncWidth | DextraSpace | DidentRight
) )
// TimeStyle enum.
type TimeStyle int
// TimeStyle kinds.
const ( const (
ET_STYLE_GO = iota ET_STYLE_GO TimeStyle = iota
ET_STYLE_HHMMSS ET_STYLE_HHMMSS
ET_STYLE_HHMM ET_STYLE_HHMM
ET_STYLE_MMSS ET_STYLE_MMSS
@ -47,35 +51,37 @@ type Statistics struct {
} }
// Decorator interface. // 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 { type Decorator interface {
Decor(*Statistics) string Decor(*Statistics) string
Syncable Syncable
} }
// Syncable interface. // Syncable interface.
// All decorators implement this interface implicitly. // All decorators implement this interface implicitly. Its Syncable
// Its Syncable method exposes width sync channel, if sync is enabled. // method exposes width sync channel, if sync is enabled.
type Syncable interface { type Syncable interface {
Syncable() (bool, chan int) Syncable() (bool, chan int)
} }
// OnCompleteMessenger interface. // 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 { type OnCompleteMessenger interface {
OnCompleteMessage(string) OnCompleteMessage(string)
} }
// AmountReceiver interface. // AmountReceiver interface.
// If decorator needs to receive increment amount, // If decorator needs to receive increment amount, so this is the right
// so this is the right interface to implement. // interface to implement.
type AmountReceiver interface { type AmountReceiver interface {
NextAmount(int, ...time.Duration) NextAmount(int, ...time.Duration)
} }
// ShutdownListener interface. // ShutdownListener interface.
// If decorator needs to be notified once upon bar shutdown event, // If decorator needs to be notified once upon bar shutdown event, so
// so this is the right interface to implement. // this is the right interface to implement.
type ShutdownListener interface { type ShutdownListener interface {
Shutdown() Shutdown()
} }
@ -90,6 +96,7 @@ var (
// WC is a struct with two public fields W and C, both of int type. // 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. // 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 { type WC struct {
W int W int
C int C int
@ -126,12 +133,13 @@ func (wc *WC) Init() {
} }
} }
// Syncable is implementation of Syncable interface.
func (wc *WC) Syncable() (bool, chan int) { func (wc *WC) Syncable() (bool, chan int) {
return (wc.C & DSyncWidth) != 0, wc.wsync return (wc.C & DSyncWidth) != 0, wc.wsync
} }
// OnComplete returns decorator, which wraps provided decorator, with sole // OnComplete returns decorator, which wraps provided decorator, with
// purpose to display provided message on complete event. // sole purpose to display provided message on complete event.
// //
// `decorator` Decorator to wrap // `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] // `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
// //
// `wcc` optional WC config // `wcc` optional WC config
func Elapsed(style int, wcc ...WC) Decorator { func Elapsed(style TimeStyle, wcc ...WC) Decorator {
var wc WC var wc WC
for _, widthConf := range wcc { for _, widthConf := range wcc {
wc = widthConf wc = widthConf
@ -26,7 +26,7 @@ func Elapsed(style int, wcc ...WC) Decorator {
type elapsedDecorator struct { type elapsedDecorator struct {
WC WC
style int style TimeStyle
startTime time.Time startTime time.Time
msg string msg string
completeMsg *string completeMsg *string

View File

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

View File

@ -6,9 +6,9 @@ import (
"github.com/VividCortex/ewma" "github.com/VividCortex/ewma"
) )
// MovingAverage is the interface that computes a moving average over a time- // MovingAverage is the interface that computes a moving average over
// series stream of numbers. The average may be over a window or exponentially // a time-series stream of numbers. The average may be over a window
// decaying. // or exponentially decaying.
type MovingAverage interface { type MovingAverage interface {
Add(float64) Add(float64)
Value() float64 Value() float64
@ -57,7 +57,8 @@ func (s *medianEwma) Add(v float64) {
s.count++ 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 { func NewMedianEwma(age ...float64) MovingAverage {
return &medianEwma{ return &medianEwma{
MovingAverage: ewma.NewMovingAverage(age...), 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...) 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 // `unit` one of [0|UnitKiB|UnitKB] zero for no unit
// //

View File

@ -1,10 +1,12 @@
package internal package internal
import "math"
// Percentage is a helper function, to calculate percentage. // Percentage is a helper function, to calculate percentage.
func Percentage(total, current, width int64) int64 { func Percentage(total, current, width int64) int64 {
if total <= 0 { if total <= 0 {
return 0 return 0
} }
p := float64(width*current) / float64(total) 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 package mpb
import ( import (
"context"
"io" "io"
"sync" "sync"
"time" "time"
"unicode/utf8"
"github.com/vbauerster/mpb/cwriter" "github.com/vbauerster/mpb/cwriter"
) )
// ProgressOption is a function option which changes the default behavior of // ProgressOption is a function option which changes the default
// progress pool, if passed to mpb.New(...ProgressOption) // behavior of progress pool, if passed to mpb.New(...ProgressOption).
type ProgressOption func(*pState) type ProgressOption func(*pState)
// WithWaitGroup provides means to have a single joint point. // WithWaitGroup provides means to have a single joint point. If
// If *sync.WaitGroup is provided, you can safely call just p.Wait() // *sync.WaitGroup is provided, you can safely call just p.Wait()
// without calling Wait() on provided *sync.WaitGroup. // without calling Wait() on provided *sync.WaitGroup. Makes sense
// Makes sense when there are more than one bar to render. // when there are more than one bar to render.
func WithWaitGroup(wg *sync.WaitGroup) ProgressOption { func WithWaitGroup(wg *sync.WaitGroup) ProgressOption {
return func(s *pState) { return func(s *pState) {
s.uwg = wg 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 { func WithWidth(w int) ProgressOption {
return func(s *pState) { return func(s *pState) {
if w >= 0 { if w >= 0 {
@ -32,16 +33,7 @@ func WithWidth(w int) ProgressOption {
} }
} }
// WithFormat overrides default bar format "[=>-]" // WithRefreshRate overrides default 120ms refresh rate.
func WithFormat(format string) ProgressOption {
return func(s *pState) {
if utf8.RuneCountInString(format) == formatLen {
s.format = format
}
}
}
// WithRefreshRate overrides default 120ms refresh rate
func WithRefreshRate(d time.Duration) ProgressOption { func WithRefreshRate(d time.Duration) ProgressOption {
return func(s *pState) { return func(s *pState) {
if d < 10*time.Millisecond { if d < 10*time.Millisecond {
@ -59,22 +51,25 @@ func WithManualRefresh(ch <-chan time.Time) ProgressOption {
} }
} }
// WithCancel provide your cancel channel, // WithContext provided context will be used for cancellation purposes.
// which you plan to close at some point. func WithContext(ctx context.Context) ProgressOption {
func WithCancel(ch <-chan struct{}) ProgressOption {
return func(s *pState) { 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 { func WithShutdownNotifier(ch chan struct{}) ProgressOption {
return func(s *pState) { return func(s *pState) {
s.shutdownNotifier = ch s.shutdownNotifier = ch
} }
} }
// WithOutput overrides default output os.Stdout // WithOutput overrides default output os.Stdout.
func WithOutput(w io.Writer) ProgressOption { func WithOutput(w io.Writer) ProgressOption {
return func(s *pState) { return func(s *pState) {
if w == nil { 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 ( import (
"container/heap" "container/heap"
"context"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
@ -17,8 +18,6 @@ const (
prr = 120 * time.Millisecond prr = 120 * time.Millisecond
// default width // default width
pwidth = 80 pwidth = 80
// default format
pformat = "[=>-]"
) )
// Progress represents the container that renders Progress bars // Progress represents the container that renders Progress bars
@ -42,24 +41,24 @@ type pState struct {
pMatrix map[int][]chan int pMatrix map[int][]chan int
aMatrix 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 uwg *sync.WaitGroup
manualRefreshCh <-chan time.Time manualRefreshCh <-chan time.Time
cancel <-chan struct{}
shutdownNotifier chan struct{} shutdownNotifier chan struct{}
waitBars map[*Bar]*Bar waitBars map[*Bar]*Bar
debugOut io.Writer debugOut io.Writer
} }
// New creates new Progress instance, which orchestrates bars rendering process. // New creates new Progress instance, which orchestrates bars rendering
// Accepts mpb.ProgressOption funcs for customization. // process. Accepts mpb.ProgressOption funcs for customization.
func New(options ...ProgressOption) *Progress { func New(options ...ProgressOption) *Progress {
pq := make(priorityQueue, 0) pq := make(priorityQueue, 0)
heap.Init(&pq) heap.Init(&pq)
s := &pState{ s := &pState{
ctx: context.Background(),
bHeap: &pq, bHeap: &pq,
width: pwidth, width: pwidth,
format: pformat,
cw: cwriter.New(os.Stdout), cw: cwriter.New(os.Stdout),
rr: prr, rr: prr,
waitBars: make(map[*Bar]*Bar), 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. // AddBar creates a new progress bar and adds to the container.
func (p *Progress) AddBar(total int64, options ...BarOption) *Bar { 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) p.wg.Add(1)
result := make(chan *Bar) result := make(chan *Bar)
select { select {
case p.operateState <- func(s *pState) { case p.operateState <- func(s *pState) {
options = append(options, barWidth(s.width), barFormat(s.format)) b := newBar(s.ctx, p.wg, filler, s.idCounter, s.width, total, options...)
b := newBar(p.wg, s.idCounter, total, s.cancel, options...)
if b.runningBar != nil { if b.runningBar != nil {
s.waitBars[b.runningBar] = b s.waitBars[b.runningBar] = b
} else { } else {
@ -106,10 +121,10 @@ func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
} }
} }
// Abort is only effective while bar progress is running, // Abort is only effective while bar progress is running, it means
// it means remove bar now without waiting for its completion. // remove bar now without waiting for its completion. If bar is already
// If bar is already completed, there is nothing to abort. // completed, there is nothing to abort. If you need to remove bar
// If you need to remove bar after completion, use BarRemoveOnComplete BarOption. // after completion, use BarRemoveOnComplete BarOption.
func (p *Progress) Abort(b *Bar, remove bool) { func (p *Progress) Abort(b *Bar, remove bool) {
select { select {
case p.operateState <- func(s *pState) { 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, // Wait first waits for user provided *sync.WaitGroup, if any, then
// then waits far all bars to complete and finally shutdowns master goroutine. // 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. // After this method has been called, there is no way to reuse *Progress
// instance.
func (p *Progress) Wait() { func (p *Progress) Wait() {
if p.uwg != nil { if p.uwg != nil {
p.uwg.Wait() p.uwg.Wait()
@ -205,8 +221,8 @@ func (s *pState) flush(lineCount int) error {
defer func() { defer func() {
if frameReader.toShutdown { if frameReader.toShutdown {
// shutdown at next flush, in other words decrement underlying WaitGroup // shutdown at next flush, in other words decrement underlying WaitGroup
// only after the bar with completed state has been flushed. // only after the bar with completed state has been flushed. this
// this ensures no bar ends up with less than 100% rendered. // ensures no bar ends up with less than 100% rendered.
s.shutdownPending = append(s.shutdownPending, bar) s.shutdownPending = append(s.shutdownPending, bar)
if replacementBar, ok := s.waitBars[bar]; ok { if replacementBar, ok := s.waitBars[bar]; ok {
heap.Push(s.bHeap, replacementBar) 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++
}

4
vendor/modules.txt vendored
View File

@ -26,7 +26,7 @@ github.com/VividCortex/ewma
github.com/containerd/continuity/pathdriver github.com/containerd/continuity/pathdriver
# github.com/containers/buildah v1.8.4 # github.com/containers/buildah v1.8.4
github.com/containers/buildah/pkg/unshare github.com/containers/buildah/pkg/unshare
# github.com/containers/image v1.5.2-0.20190620105408-93b1deece293 # github.com/containers/image v1.5.2-0.20190717062552-2178abd5f9b1
github.com/containers/image/copy github.com/containers/image/copy
github.com/containers/image/directory github.com/containers/image/directory
github.com/containers/image/docker github.com/containers/image/docker
@ -226,7 +226,7 @@ github.com/urfave/cli
github.com/vbatts/tar-split/tar/asm github.com/vbatts/tar-split/tar/asm
github.com/vbatts/tar-split/tar/storage github.com/vbatts/tar-split/tar/storage
github.com/vbatts/tar-split/archive/tar github.com/vbatts/tar-split/archive/tar
# github.com/vbauerster/mpb v3.3.4+incompatible # github.com/vbauerster/mpb v3.4.0+incompatible
github.com/vbauerster/mpb github.com/vbauerster/mpb
github.com/vbauerster/mpb/decor github.com/vbauerster/mpb/decor
github.com/vbauerster/mpb/cwriter github.com/vbauerster/mpb/cwriter