summaryrefslogtreecommitdiff
path: root/vendor/github.com/schollz
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/schollz')
-rw-r--r--vendor/github.com/schollz/progressbar/v3/.gitignore18
-rw-r--r--vendor/github.com/schollz/progressbar/v3/.golangci.yml21
-rw-r--r--vendor/github.com/schollz/progressbar/v3/.travis.yml6
-rw-r--r--vendor/github.com/schollz/progressbar/v3/LICENSE21
-rw-r--r--vendor/github.com/schollz/progressbar/v3/README.md121
-rw-r--r--vendor/github.com/schollz/progressbar/v3/progressbar.go1490
-rw-r--r--vendor/github.com/schollz/progressbar/v3/spinners.go80
7 files changed, 1757 insertions, 0 deletions
diff --git a/vendor/github.com/schollz/progressbar/v3/.gitignore b/vendor/github.com/schollz/progressbar/v3/.gitignore
new file mode 100644
index 0000000..35c3bde
--- /dev/null
+++ b/vendor/github.com/schollz/progressbar/v3/.gitignore
@@ -0,0 +1,18 @@
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+
+# Test binary, built with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+
+# Dependency directories (remove the comment below to include it)
+# vendor/
+
+.idea/
+*.tar.gz
diff --git a/vendor/github.com/schollz/progressbar/v3/.golangci.yml b/vendor/github.com/schollz/progressbar/v3/.golangci.yml
new file mode 100644
index 0000000..8c45095
--- /dev/null
+++ b/vendor/github.com/schollz/progressbar/v3/.golangci.yml
@@ -0,0 +1,21 @@
+run:
+ timeout: 5m
+ exclude-dirs:
+ - vendor
+ - examples
+
+linters:
+ enable:
+ - errcheck
+ - gocyclo
+ - gofmt
+ - goimports
+ - gosimple
+ - govet
+ - ineffassign
+ - staticcheck
+ - unused
+
+linters-settings:
+ gocyclo:
+ min-complexity: 20 \ No newline at end of file
diff --git a/vendor/github.com/schollz/progressbar/v3/.travis.yml b/vendor/github.com/schollz/progressbar/v3/.travis.yml
new file mode 100644
index 0000000..1d23387
--- /dev/null
+++ b/vendor/github.com/schollz/progressbar/v3/.travis.yml
@@ -0,0 +1,6 @@
+language: go
+
+go:
+ - tip
+
+script: go test -v .
diff --git a/vendor/github.com/schollz/progressbar/v3/LICENSE b/vendor/github.com/schollz/progressbar/v3/LICENSE
new file mode 100644
index 0000000..0ca9765
--- /dev/null
+++ b/vendor/github.com/schollz/progressbar/v3/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Zack
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/schollz/progressbar/v3/README.md b/vendor/github.com/schollz/progressbar/v3/README.md
new file mode 100644
index 0000000..e94a1c1
--- /dev/null
+++ b/vendor/github.com/schollz/progressbar/v3/README.md
@@ -0,0 +1,121 @@
+# progressbar
+
+[![CI](https://github.com/schollz/progressbar/actions/workflows/ci.yml/badge.svg?branch=main&event=push)](https://github.com/schollz/progressbar/actions/workflows/ci.yml)
+[![go report card](https://goreportcard.com/badge/github.com/schollz/progressbar)](https://goreportcard.com/report/github.com/schollz/progressbar)
+[![coverage](https://img.shields.io/badge/coverage-84%25-brightgreen.svg)](https://gocover.io/github.com/schollz/progressbar)
+[![godocs](https://godoc.org/github.com/schollz/progressbar?status.svg)](https://godoc.org/github.com/schollz/progressbar/v3)
+
+A very simple thread-safe progress bar which should work on every OS without problems. I needed a progressbar for [croc](https://github.com/schollz/croc) and everything I tried had problems, so I made another one. In order to be OS agnostic I do not plan to support [multi-line outputs](https://github.com/schollz/progressbar/issues/6).
+
+
+## Install
+
+```
+go get -u github.com/schollz/progressbar/v3
+```
+
+## Usage
+
+### Basic usage
+
+```golang
+bar := progressbar.Default(100)
+for i := 0; i < 100; i++ {
+ bar.Add(1)
+ time.Sleep(40 * time.Millisecond)
+}
+```
+
+which looks like:
+
+![Example of basic bar](examples/basic/basic.gif)
+
+
+### I/O operations
+
+The `progressbar` implements an `io.Writer` so it can automatically detect the number of bytes written to a stream, so you can use it as a progressbar for an `io.Reader`.
+
+```golang
+req, _ := http.NewRequest("GET", "https://dl.google.com/go/go1.14.2.src.tar.gz", nil)
+resp, _ := http.DefaultClient.Do(req)
+defer resp.Body.Close()
+
+f, _ := os.OpenFile("go1.14.2.src.tar.gz", os.O_CREATE|os.O_WRONLY, 0644)
+defer f.Close()
+
+bar := progressbar.DefaultBytes(
+ resp.ContentLength,
+ "downloading",
+)
+io.Copy(io.MultiWriter(f, bar), resp.Body)
+```
+
+which looks like:
+
+![Example of download bar](examples/download/download.gif)
+
+
+### Progress bar with unknown length
+
+A progressbar with unknown length is a spinner. Any bar with -1 length will automatically convert it to a spinner with a customizable spinner type. For example, the above code can be run and set the `resp.ContentLength` to `-1`.
+
+which looks like:
+
+![Example of download bar with unknown length](examples/download-unknown/download-unknown.gif)
+
+
+### Customization
+
+There is a lot of customization that you can do - change the writer, the color, the width, description, theme, etc. See [all the options](https://pkg.go.dev/github.com/schollz/progressbar/v3?tab=doc#Option).
+
+```golang
+bar := progressbar.NewOptions(1000,
+ progressbar.OptionSetWriter(ansi.NewAnsiStdout()), //you should install "github.com/k0kubun/go-ansi"
+ progressbar.OptionEnableColorCodes(true),
+ progressbar.OptionShowBytes(true),
+ progressbar.OptionSetWidth(15),
+ progressbar.OptionSetDescription("[cyan][1/3][reset] Writing moshable file..."),
+ progressbar.OptionSetTheme(progressbar.Theme{
+ Saucer: "[green]=[reset]",
+ SaucerHead: "[green]>[reset]",
+ SaucerPadding: " ",
+ BarStart: "[",
+ BarEnd: "]",
+ }))
+for i := 0; i < 1000; i++ {
+ bar.Add(1)
+ time.Sleep(5 * time.Millisecond)
+}
+```
+
+which looks like:
+
+![Example of customized bar](examples/customization/customization.gif)
+
+
+## Contributing
+
+Pull requests are welcome. Feel free to...
+
+- Revise documentation
+- Add new features
+- Fix bugs
+- Suggest improvements
+
+## Thanks
+
+Thanks [@Dynom](https://github.com/dynom) for massive improvements in version 2.0!
+
+Thanks [@CrushedPixel](https://github.com/CrushedPixel) for adding descriptions and color code support!
+
+Thanks [@MrMe42](https://github.com/MrMe42) for adding some minor features!
+
+Thanks [@tehstun](https://github.com/tehstun) for some great PRs!
+
+Thanks [@Benzammour](https://github.com/Benzammour) and [@haseth](https://github.com/haseth) for helping create v3!
+
+Thanks [@briandowns](https://github.com/briandowns) for compiling the list of spinners.
+
+## License
+
+MIT
diff --git a/vendor/github.com/schollz/progressbar/v3/progressbar.go b/vendor/github.com/schollz/progressbar/v3/progressbar.go
new file mode 100644
index 0000000..0ccec7d
--- /dev/null
+++ b/vendor/github.com/schollz/progressbar/v3/progressbar.go
@@ -0,0 +1,1490 @@
+package progressbar
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "log"
+ "math"
+ "net/http"
+ "os"
+ "regexp"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/mitchellh/colorstring"
+ "github.com/rivo/uniseg"
+ "golang.org/x/term"
+)
+
+// ProgressBar is a thread-safe, simple
+// progress bar
+type ProgressBar struct {
+ state state
+ config config
+ lock sync.Mutex
+}
+
+// State is the basic properties of the bar
+type State struct {
+ Max int64
+ CurrentNum int64
+ CurrentPercent float64
+ CurrentBytes float64
+ SecondsSince float64
+ SecondsLeft float64
+ KBsPerSecond float64
+ Description string
+}
+
+type state struct {
+ currentNum int64
+ currentPercent int
+ lastPercent int
+ currentSaucerSize int
+ isAltSaucerHead bool
+
+ lastShown time.Time
+ startTime time.Time // time when the progress bar start working
+
+ counterTime time.Time
+ counterNumSinceLast int64
+ counterLastTenRates []float64
+ spinnerIdx int // the index of spinner
+
+ maxLineWidth int
+ currentBytes float64
+ finished bool
+ exit bool // Progress bar exit halfway
+
+ details []string // details to show,only used when detail row is set to more than 0
+
+ rendered string
+}
+
+type config struct {
+ max int64 // max number of the counter
+ maxHumanized string
+ maxHumanizedSuffix string
+ width int
+ writer io.Writer
+ theme Theme
+ renderWithBlankState bool
+ description string
+ iterationString string
+ ignoreLength bool // ignoreLength if max bytes not known
+
+ // whether the output is expected to contain color codes
+ colorCodes bool
+
+ // show rate of change in kB/sec or MB/sec
+ showBytes bool
+ // show the iterations per second
+ showIterationsPerSecond bool
+ showIterationsCount bool
+
+ // whether the progress bar should show the total bytes (e.g. 23/24 or 23/-, vs. just 23).
+ showTotalBytes bool
+
+ // whether the progress bar should show elapsed time.
+ // always enabled if predictTime is true.
+ elapsedTime bool
+
+ showElapsedTimeOnFinish bool
+
+ // whether the progress bar should attempt to predict the finishing
+ // time of the progress based on the start time and the average
+ // number of seconds between increments.
+ predictTime bool
+
+ // minimum time to wait in between updates
+ throttleDuration time.Duration
+
+ // clear bar once finished
+ clearOnFinish bool
+
+ // spinnerType should be a number between 0-75
+ spinnerType int
+
+ // spinnerTypeOptionUsed remembers if the spinnerType was changed manually
+ spinnerTypeOptionUsed bool
+
+ // spinnerChangeInterval the change interval of spinner
+ // if set this attribute to 0, the spinner only change when renderProgressBar was called
+ // for example, each time when Add() was called,which will call renderProgressBar function
+ spinnerChangeInterval time.Duration
+
+ // spinner represents the spinner as a slice of string
+ spinner []string
+
+ // fullWidth specifies whether to measure and set the bar to a specific width
+ fullWidth bool
+
+ // invisible doesn't render the bar at all, useful for debugging
+ invisible bool
+
+ onCompletion func()
+
+ // whether the render function should make use of ANSI codes to reduce console I/O
+ useANSICodes bool
+
+ // whether to use the IEC units (e.g. MiB) instead of the default SI units (e.g. MB)
+ useIECUnits bool
+
+ // showDescriptionAtLineEnd specifies whether description should be written at line end instead of line start
+ showDescriptionAtLineEnd bool
+
+ // specifies how many rows of details to show,default value is 0 and no details will be shown
+ maxDetailRow int
+
+ stdBuffer bytes.Buffer
+}
+
+// Theme defines the elements of the bar
+type Theme struct {
+ Saucer string
+ AltSaucerHead string
+ SaucerHead string
+ SaucerPadding string
+ BarStart string
+ BarEnd string
+
+ // BarStartFilled is used after the Bar starts filling, if set. Otherwise, it defaults to BarStart.
+ BarStartFilled string
+
+ // BarEndFilled is used once the Bar finishes, if set. Otherwise, it defaults to BarEnd.
+ BarEndFilled string
+}
+
+var (
+ // ThemeDefault is given by default (if not changed with OptionSetTheme), and it looks like "|████ |".
+ ThemeDefault = Theme{Saucer: "█", SaucerPadding: " ", BarStart: "|", BarEnd: "|"}
+
+ // ThemeASCII is a predefined Theme that uses ASCII symbols. It looks like "[===>...]".
+ // Configure it with OptionSetTheme(ThemeASCII).
+ ThemeASCII = Theme{
+ Saucer: "=",
+ SaucerHead: ">",
+ SaucerPadding: ".",
+ BarStart: "[",
+ BarEnd: "]",
+ }
+
+ // ThemeUnicode is a predefined Theme that uses Unicode characters, displaying a graphic bar.
+ // It looks like "" (rendering will depend on font being used).
+ // It requires special symbols usually found in "nerd fonts" [2], or in Fira Code [1], and other sources.
+ // Configure it with OptionSetTheme(ThemeUnicode).
+ //
+ // [1] https://github.com/tonsky/FiraCode
+ // [2] https://www.nerdfonts.com/
+ ThemeUnicode = Theme{
+ Saucer: "\uEE04", // 
+ SaucerHead: "\uEE04", // 
+ SaucerPadding: "\uEE01", // 
+ BarStart: "\uEE00", // 
+ BarStartFilled: "\uEE03", // 
+ BarEnd: "\uEE02", // 
+ BarEndFilled: "\uEE05", // 
+ }
+)
+
+// Option is the type all options need to adhere to
+type Option func(p *ProgressBar)
+
+// OptionSetWidth sets the width of the bar
+func OptionSetWidth(s int) Option {
+ return func(p *ProgressBar) {
+ p.config.width = s
+ }
+}
+
+// OptionSetSpinnerChangeInterval sets the spinner change interval
+// the spinner will change according to this value.
+// By default, this value is 100 * time.Millisecond
+// If you don't want to let this progressbar update by specified time interval
+// you can set this value to zero, then the spinner will change each time rendered,
+// such as when Add() or Describe() was called
+func OptionSetSpinnerChangeInterval(interval time.Duration) Option {
+ return func(p *ProgressBar) {
+ p.config.spinnerChangeInterval = interval
+ }
+}
+
+// OptionSpinnerType sets the type of spinner used for indeterminate bars
+func OptionSpinnerType(spinnerType int) Option {
+ return func(p *ProgressBar) {
+ p.config.spinnerTypeOptionUsed = true
+ p.config.spinnerType = spinnerType
+ }
+}
+
+// OptionSpinnerCustom sets the spinner used for indeterminate bars to the passed
+// slice of string
+func OptionSpinnerCustom(spinner []string) Option {
+ return func(p *ProgressBar) {
+ p.config.spinner = spinner
+ }
+}
+
+// OptionSetTheme sets the elements the bar is constructed with.
+// There are two pre-defined themes you can use: ThemeASCII and ThemeUnicode.
+func OptionSetTheme(t Theme) Option {
+ return func(p *ProgressBar) {
+ p.config.theme = t
+ }
+}
+
+// OptionSetVisibility sets the visibility
+func OptionSetVisibility(visibility bool) Option {
+ return func(p *ProgressBar) {
+ p.config.invisible = !visibility
+ }
+}
+
+// OptionFullWidth sets the bar to be full width
+func OptionFullWidth() Option {
+ return func(p *ProgressBar) {
+ p.config.fullWidth = true
+ }
+}
+
+// OptionSetWriter sets the output writer (defaults to os.StdOut)
+func OptionSetWriter(w io.Writer) Option {
+ return func(p *ProgressBar) {
+ p.config.writer = w
+ }
+}
+
+// OptionSetRenderBlankState sets whether or not to render a 0% bar on construction
+func OptionSetRenderBlankState(r bool) Option {
+ return func(p *ProgressBar) {
+ p.config.renderWithBlankState = r
+ }
+}
+
+// OptionSetDescription sets the description of the bar to render in front of it
+func OptionSetDescription(description string) Option {
+ return func(p *ProgressBar) {
+ p.config.description = description
+ }
+}
+
+// OptionEnableColorCodes enables or disables support for color codes
+// using mitchellh/colorstring
+func OptionEnableColorCodes(colorCodes bool) Option {
+ return func(p *ProgressBar) {
+ p.config.colorCodes = colorCodes
+ }
+}
+
+// OptionSetElapsedTime will enable elapsed time. Always enabled if OptionSetPredictTime is true.
+func OptionSetElapsedTime(elapsedTime bool) Option {
+ return func(p *ProgressBar) {
+ p.config.elapsedTime = elapsedTime
+ }
+}
+
+// OptionSetPredictTime will also attempt to predict the time remaining.
+func OptionSetPredictTime(predictTime bool) Option {
+ return func(p *ProgressBar) {
+ p.config.predictTime = predictTime
+ }
+}
+
+// OptionShowCount will also print current count out of total
+func OptionShowCount() Option {
+ return func(p *ProgressBar) {
+ p.config.showIterationsCount = true
+ }
+}
+
+// OptionShowIts will also print the iterations/second
+func OptionShowIts() Option {
+ return func(p *ProgressBar) {
+ p.config.showIterationsPerSecond = true
+ }
+}
+
+// OptionShowElapsedTimeOnFinish will keep the display of elapsed time on finish.
+func OptionShowElapsedTimeOnFinish() Option {
+ return func(p *ProgressBar) {
+ p.config.showElapsedTimeOnFinish = true
+ }
+}
+
+// OptionShowTotalBytes will keep the display of total bytes.
+func OptionShowTotalBytes(flag bool) Option {
+ return func(p *ProgressBar) {
+ p.config.showTotalBytes = flag
+ }
+}
+
+// OptionSetItsString sets what's displayed for iterations a second. The default is "it" which would display: "it/s"
+func OptionSetItsString(iterationString string) Option {
+ return func(p *ProgressBar) {
+ p.config.iterationString = iterationString
+ }
+}
+
+// OptionThrottle will wait the specified duration before updating again. The default
+// duration is 0 seconds.
+func OptionThrottle(duration time.Duration) Option {
+ return func(p *ProgressBar) {
+ p.config.throttleDuration = duration
+ }
+}
+
+// OptionClearOnFinish will clear the bar once its finished.
+func OptionClearOnFinish() Option {
+ return func(p *ProgressBar) {
+ p.config.clearOnFinish = true
+ }
+}
+
+// OptionOnCompletion will invoke cmpl function once its finished
+func OptionOnCompletion(cmpl func()) Option {
+ return func(p *ProgressBar) {
+ p.config.onCompletion = cmpl
+ }
+}
+
+// OptionShowBytes will update the progress bar
+// configuration settings to display/hide kBytes/Sec
+func OptionShowBytes(val bool) Option {
+ return func(p *ProgressBar) {
+ p.config.showBytes = val
+ }
+}
+
+// OptionUseANSICodes will use more optimized terminal i/o.
+//
+// Only useful in environments with support for ANSI escape sequences.
+func OptionUseANSICodes(val bool) Option {
+ return func(p *ProgressBar) {
+ p.config.useANSICodes = val
+ }
+}
+
+// OptionUseIECUnits will enable IEC units (e.g. MiB) instead of the default
+// SI units (e.g. MB).
+func OptionUseIECUnits(val bool) Option {
+ return func(p *ProgressBar) {
+ p.config.useIECUnits = val
+ }
+}
+
+// OptionShowDescriptionAtLineEnd defines whether description should be written at line end instead of line start
+func OptionShowDescriptionAtLineEnd() Option {
+ return func(p *ProgressBar) {
+ p.config.showDescriptionAtLineEnd = true
+ }
+}
+
+// OptionSetMaxDetailRow sets the max row of details
+// the row count should be less than the terminal height, otherwise it will not give you the output you want
+func OptionSetMaxDetailRow(row int) Option {
+ return func(p *ProgressBar) {
+ p.config.maxDetailRow = row
+ }
+}
+
+// NewOptions constructs a new instance of ProgressBar, with any options you specify
+func NewOptions(max int, options ...Option) *ProgressBar {
+ return NewOptions64(int64(max), options...)
+}
+
+// NewOptions64 constructs a new instance of ProgressBar, with any options you specify
+func NewOptions64(max int64, options ...Option) *ProgressBar {
+ b := ProgressBar{
+ state: state{
+ startTime: time.Time{},
+ lastShown: time.Time{},
+ counterTime: time.Time{},
+ },
+ config: config{
+ writer: os.Stdout,
+ theme: ThemeDefault,
+ iterationString: "it",
+ width: 40,
+ max: max,
+ throttleDuration: 0 * time.Nanosecond,
+ elapsedTime: max == -1,
+ predictTime: true,
+ spinnerType: 9,
+ invisible: false,
+ spinnerChangeInterval: 100 * time.Millisecond,
+ showTotalBytes: true,
+ },
+ }
+
+ for _, o := range options {
+ o(&b)
+ }
+
+ if b.config.spinnerType < 0 || b.config.spinnerType > 75 {
+ panic("invalid spinner type, must be between 0 and 75")
+ }
+
+ if b.config.maxDetailRow < 0 {
+ panic("invalid max detail row, must be greater than 0")
+ }
+
+ // ignoreLength if max bytes not known
+ if b.config.max == -1 {
+ b.lengthUnknown()
+ }
+
+ b.config.maxHumanized, b.config.maxHumanizedSuffix = humanizeBytes(float64(b.config.max),
+ b.config.useIECUnits)
+
+ if b.config.renderWithBlankState {
+ b.RenderBlank()
+ }
+
+ // if the render time interval attribute is set
+ if b.config.spinnerChangeInterval != 0 && !b.config.invisible && b.config.ignoreLength {
+ go func() {
+ ticker := time.NewTicker(b.config.spinnerChangeInterval)
+ defer ticker.Stop()
+ for {
+ select {
+ case <-ticker.C:
+ if b.IsFinished() {
+ return
+ }
+ if b.IsStarted() {
+ b.lock.Lock()
+ b.render()
+ b.lock.Unlock()
+ }
+ }
+ }
+ }()
+ }
+
+ return &b
+}
+
+func getBasicState() state {
+ now := time.Now()
+ return state{
+ startTime: now,
+ lastShown: now,
+ counterTime: now,
+ }
+}
+
+// New returns a new ProgressBar
+// with the specified maximum
+func New(max int) *ProgressBar {
+ return NewOptions(max)
+}
+
+// DefaultBytes provides a progressbar to measure byte
+// throughput with recommended defaults.
+// Set maxBytes to -1 to use as a spinner.
+func DefaultBytes(maxBytes int64, description ...string) *ProgressBar {
+ desc := ""
+ if len(description) > 0 {
+ desc = description[0]
+ }
+ return NewOptions64(
+ maxBytes,
+ OptionSetDescription(desc),
+ OptionSetWriter(os.Stderr),
+ OptionShowBytes(true),
+ OptionShowTotalBytes(true),
+ OptionSetWidth(10),
+ OptionThrottle(65*time.Millisecond),
+ OptionShowCount(),
+ OptionOnCompletion(func() {
+ fmt.Fprint(os.Stderr, "\n")
+ }),
+ OptionSpinnerType(14),
+ OptionFullWidth(),
+ OptionSetRenderBlankState(true),
+ )
+}
+
+// DefaultBytesSilent is the same as DefaultBytes, but does not output anywhere.
+// String() can be used to get the output instead.
+func DefaultBytesSilent(maxBytes int64, description ...string) *ProgressBar {
+ // Mostly the same bar as DefaultBytes
+
+ desc := ""
+ if len(description) > 0 {
+ desc = description[0]
+ }
+ return NewOptions64(
+ maxBytes,
+ OptionSetDescription(desc),
+ OptionSetWriter(io.Discard),
+ OptionShowBytes(true),
+ OptionShowTotalBytes(true),
+ OptionSetWidth(10),
+ OptionThrottle(65*time.Millisecond),
+ OptionShowCount(),
+ OptionSpinnerType(14),
+ OptionFullWidth(),
+ )
+}
+
+// Default provides a progressbar with recommended defaults.
+// Set max to -1 to use as a spinner.
+func Default(max int64, description ...string) *ProgressBar {
+ desc := ""
+ if len(description) > 0 {
+ desc = description[0]
+ }
+ return NewOptions64(
+ max,
+ OptionSetDescription(desc),
+ OptionSetWriter(os.Stderr),
+ OptionSetWidth(10),
+ OptionShowTotalBytes(true),
+ OptionThrottle(65*time.Millisecond),
+ OptionShowCount(),
+ OptionShowIts(),
+ OptionOnCompletion(func() {
+ fmt.Fprint(os.Stderr, "\n")
+ }),
+ OptionSpinnerType(14),
+ OptionFullWidth(),
+ OptionSetRenderBlankState(true),
+ )
+}
+
+// DefaultSilent is the same as Default, but does not output anywhere.
+// String() can be used to get the output instead.
+func DefaultSilent(max int64, description ...string) *ProgressBar {
+ // Mostly the same bar as Default
+
+ desc := ""
+ if len(description) > 0 {
+ desc = description[0]
+ }
+ return NewOptions64(
+ max,
+ OptionSetDescription(desc),
+ OptionSetWriter(io.Discard),
+ OptionSetWidth(10),
+ OptionShowTotalBytes(true),
+ OptionThrottle(65*time.Millisecond),
+ OptionShowCount(),
+ OptionShowIts(),
+ OptionSpinnerType(14),
+ OptionFullWidth(),
+ )
+}
+
+// String returns the current rendered version of the progress bar.
+// It will never return an empty string while the progress bar is running.
+func (p *ProgressBar) String() string {
+ return p.state.rendered
+}
+
+// RenderBlank renders the current bar state, you can use this to render a 0% state
+func (p *ProgressBar) RenderBlank() error {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ if p.config.invisible {
+ return nil
+ }
+ if p.state.currentNum == 0 {
+ p.state.lastShown = time.Time{}
+ }
+ return p.render()
+}
+
+// StartWithoutRender will start the progress bar without rendering it
+// this method is created for the use case where you want to start the progress
+// but don't want to render it immediately.
+// If you want to start the progress and render it immediately, use RenderBlank instead,
+// or maybe you can use Add to start it automatically, but it will make the time calculation less precise.
+func (p *ProgressBar) StartWithoutRender() {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ if p.IsStarted() {
+ return
+ }
+
+ p.state.startTime = time.Now()
+ // the counterTime should be set to the current time
+ p.state.counterTime = time.Now()
+}
+
+// Reset will reset the clock that is used
+// to calculate current time and the time left.
+func (p *ProgressBar) Reset() {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ p.state = getBasicState()
+}
+
+// Finish will fill the bar to full
+func (p *ProgressBar) Finish() error {
+ p.lock.Lock()
+ p.state.currentNum = p.config.max
+ if !p.config.ignoreLength {
+ p.state.currentBytes = float64(p.config.max)
+ }
+ p.lock.Unlock()
+ return p.Add(0)
+}
+
+// Exit will exit the bar to keep current state
+func (p *ProgressBar) Exit() error {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ p.state.exit = true
+ if p.config.onCompletion != nil {
+ p.config.onCompletion()
+ }
+ return nil
+}
+
+// Add will add the specified amount to the progressbar
+func (p *ProgressBar) Add(num int) error {
+ return p.Add64(int64(num))
+}
+
+// Set will set the bar to a current number
+func (p *ProgressBar) Set(num int) error {
+ return p.Set64(int64(num))
+}
+
+// Set64 will set the bar to a current number
+func (p *ProgressBar) Set64(num int64) error {
+ p.lock.Lock()
+ toAdd := num - int64(p.state.currentBytes)
+ p.lock.Unlock()
+ return p.Add64(toAdd)
+}
+
+// Add64 will add the specified amount to the progressbar
+func (p *ProgressBar) Add64(num int64) error {
+ if p.config.invisible {
+ return nil
+ }
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ if p.state.exit {
+ return nil
+ }
+
+ // error out since OptionSpinnerCustom will always override a manually set spinnerType
+ if p.config.spinnerTypeOptionUsed && len(p.config.spinner) > 0 {
+ return errors.New("OptionSpinnerType and OptionSpinnerCustom cannot be used together")
+ }
+
+ if p.config.max == 0 {
+ return errors.New("max must be greater than 0")
+ }
+
+ if p.state.currentNum < p.config.max {
+ if p.config.ignoreLength {
+ p.state.currentNum = (p.state.currentNum + num) % p.config.max
+ } else {
+ p.state.currentNum += num
+ }
+ }
+
+ p.state.currentBytes += float64(num)
+
+ if p.state.counterTime.IsZero() {
+ p.state.counterTime = time.Now()
+ }
+
+ // reset the countdown timer every second to take rolling average
+ p.state.counterNumSinceLast += num
+ if time.Since(p.state.counterTime).Seconds() > 0.5 {
+ p.state.counterLastTenRates = append(p.state.counterLastTenRates, float64(p.state.counterNumSinceLast)/time.Since(p.state.counterTime).Seconds())
+ if len(p.state.counterLastTenRates) > 10 {
+ p.state.counterLastTenRates = p.state.counterLastTenRates[1:]
+ }
+ p.state.counterTime = time.Now()
+ p.state.counterNumSinceLast = 0
+ }
+
+ percent := float64(p.state.currentNum) / float64(p.config.max)
+ p.state.currentSaucerSize = int(percent * float64(p.config.width))
+ p.state.currentPercent = int(percent * 100)
+ updateBar := p.state.currentPercent != p.state.lastPercent && p.state.currentPercent > 0
+
+ p.state.lastPercent = p.state.currentPercent
+ if p.state.currentNum > p.config.max {
+ return errors.New("current number exceeds max")
+ }
+
+ // always update if show bytes/second or its/second
+ if updateBar || p.config.showIterationsPerSecond || p.config.showIterationsCount {
+ return p.render()
+ }
+
+ return nil
+}
+
+// AddDetail adds a detail to the progress bar. Only used when maxDetailRow is set to a value greater than 0
+func (p *ProgressBar) AddDetail(detail string) error {
+ if p.config.maxDetailRow == 0 {
+ return errors.New("maxDetailRow is set to 0, cannot add detail")
+ }
+ if p.IsFinished() {
+ return errors.New("cannot add detail to a finished progress bar")
+ }
+
+ p.lock.Lock()
+ defer p.lock.Unlock()
+ if p.state.details == nil {
+ // if we add a detail before the first add, it will be weird that we have detail but don't have the progress bar in the top.
+ // so when we add the first detail, we will render the progress bar first.
+ if err := p.render(); err != nil {
+ return err
+ }
+ }
+ p.state.details = append(p.state.details, detail)
+ if len(p.state.details) > p.config.maxDetailRow {
+ p.state.details = p.state.details[1:]
+ }
+ if err := p.renderDetails(); err != nil {
+ return err
+ }
+ return nil
+}
+
+// renderDetails renders the details of the progress bar
+func (p *ProgressBar) renderDetails() error {
+ if p.config.invisible {
+ return nil
+ }
+ if p.state.finished {
+ return nil
+ }
+ if p.config.maxDetailRow == 0 {
+ return nil
+ }
+
+ b := strings.Builder{}
+ b.WriteString("\n")
+
+ // render the details row
+ for _, detail := range p.state.details {
+ b.WriteString(fmt.Sprintf("\u001B[K\r%s\n", detail))
+ }
+ // add empty lines to fill the maxDetailRow
+ for i := len(p.state.details); i < p.config.maxDetailRow; i++ {
+ b.WriteString("\u001B[K\n")
+ }
+
+ // move the cursor up to the start of the details row
+ b.WriteString(fmt.Sprintf("\u001B[%dF", p.config.maxDetailRow+1))
+
+ writeString(p.config, b.String())
+
+ return nil
+}
+
+// Clear erases the progress bar from the current line
+func (p *ProgressBar) Clear() error {
+ return clearProgressBar(p.config, p.state)
+}
+
+// Describe will change the description shown before the progress, which
+// can be changed on the fly (as for a slow running process).
+func (p *ProgressBar) Describe(description string) {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+ p.config.description = description
+ if p.config.invisible {
+ return
+ }
+ p.render()
+}
+
+// New64 returns a new ProgressBar
+// with the specified maximum
+func New64(max int64) *ProgressBar {
+ return NewOptions64(max)
+}
+
+// GetMax returns the max of a bar
+func (p *ProgressBar) GetMax() int {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ return int(p.config.max)
+}
+
+// GetMax64 returns the current max
+func (p *ProgressBar) GetMax64() int64 {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ return p.config.max
+}
+
+// ChangeMax takes in a int
+// and changes the max value
+// of the progress bar
+func (p *ProgressBar) ChangeMax(newMax int) {
+ p.ChangeMax64(int64(newMax))
+}
+
+// ChangeMax64 is basically
+// the same as ChangeMax,
+// but takes in a int64
+// to avoid casting
+func (p *ProgressBar) ChangeMax64(newMax int64) {
+ p.lock.Lock()
+
+ p.config.max = newMax
+
+ if p.config.showBytes {
+ p.config.maxHumanized, p.config.maxHumanizedSuffix = humanizeBytes(float64(p.config.max),
+ p.config.useIECUnits)
+ }
+
+ if newMax == -1 {
+ p.lengthUnknown()
+ } else {
+ p.lengthKnown(newMax)
+ }
+ p.lock.Unlock() // so p.Add can lock
+
+ p.Add(0) // re-render
+}
+
+// AddMax takes in a int
+// and adds it to the max
+// value of the progress bar
+func (p *ProgressBar) AddMax(added int) {
+ p.AddMax64(int64(added))
+}
+
+// AddMax64 is basically
+// the same as AddMax,
+// but takes in a int64
+// to avoid casting
+func (p *ProgressBar) AddMax64(added int64) {
+ p.lock.Lock()
+
+ p.config.max += added
+
+ if p.config.showBytes {
+ p.config.maxHumanized, p.config.maxHumanizedSuffix = humanizeBytes(float64(p.config.max),
+ p.config.useIECUnits)
+ }
+
+ if p.config.max == -1 {
+ p.lengthUnknown()
+ } else {
+ p.lengthKnown(p.config.max)
+ }
+ p.lock.Unlock() // so p.Add can lock
+
+ p.Add(0) // re-render
+}
+
+// IsFinished returns true if progress bar is completed
+func (p *ProgressBar) IsFinished() bool {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ return p.state.finished
+}
+
+// IsStarted returns true if progress bar is started
+func (p *ProgressBar) IsStarted() bool {
+ return !p.state.startTime.IsZero()
+}
+
+// render renders the progress bar, updating the maximum
+// rendered line width. this function is not thread-safe,
+// so it must be called with an acquired lock.
+func (p *ProgressBar) render() error {
+ // make sure that the rendering is not happening too quickly
+ // but always show if the currentNum reaches the max
+ if !p.IsStarted() {
+ p.state.startTime = time.Now()
+ } else if time.Since(p.state.lastShown).Nanoseconds() < p.config.throttleDuration.Nanoseconds() &&
+ p.state.currentNum < p.config.max {
+ return nil
+ }
+
+ if !p.config.useANSICodes {
+ // first, clear the existing progress bar, if not yet finished.
+ if !p.state.finished {
+ err := clearProgressBar(p.config, p.state)
+ if err != nil {
+ return err
+ }
+ }
+ }
+
+ // check if the progress bar is finished
+ if !p.state.finished && p.state.currentNum >= p.config.max {
+ p.state.finished = true
+ if !p.config.clearOnFinish {
+ io.Copy(p.config.writer, &p.config.stdBuffer)
+ renderProgressBar(p.config, &p.state)
+ }
+ if p.config.maxDetailRow > 0 {
+ p.renderDetails()
+ // put the cursor back to the last line of the details
+ writeString(p.config, fmt.Sprintf("\u001B[%dB\r\u001B[%dC", p.config.maxDetailRow, len(p.state.details[len(p.state.details)-1])))
+ }
+ if p.config.onCompletion != nil {
+ p.config.onCompletion()
+ }
+ }
+ if p.state.finished {
+ // when using ANSI codes we don't pre-clean the current line
+ if p.config.useANSICodes && p.config.clearOnFinish {
+ err := clearProgressBar(p.config, p.state)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+ }
+
+ // then, re-render the current progress bar
+ io.Copy(p.config.writer, &p.config.stdBuffer)
+ w, err := renderProgressBar(p.config, &p.state)
+ if err != nil {
+ return err
+ }
+
+ if w > p.state.maxLineWidth {
+ p.state.maxLineWidth = w
+ }
+
+ p.state.lastShown = time.Now()
+
+ return nil
+}
+
+// lengthUnknown sets the progress bar to ignore the length
+func (p *ProgressBar) lengthUnknown() {
+ p.config.ignoreLength = true
+ p.config.max = int64(p.config.width)
+ p.config.predictTime = false
+}
+
+// lengthKnown sets the progress bar to do not ignore the length
+func (p *ProgressBar) lengthKnown(max int64) {
+ p.config.ignoreLength = false
+ p.config.max = max
+ p.config.predictTime = true
+}
+
+// State returns the current state
+func (p *ProgressBar) State() State {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+ s := State{}
+ s.CurrentNum = p.state.currentNum
+ s.Max = p.config.max
+ if p.config.ignoreLength {
+ s.Max = -1
+ }
+ s.CurrentPercent = float64(p.state.currentNum) / float64(p.config.max)
+ s.CurrentBytes = p.state.currentBytes
+ if p.IsStarted() {
+ s.SecondsSince = time.Since(p.state.startTime).Seconds()
+ } else {
+ s.SecondsSince = 0
+ }
+
+ if p.state.currentNum > 0 {
+ s.SecondsLeft = s.SecondsSince / float64(p.state.currentNum) * (float64(p.config.max) - float64(p.state.currentNum))
+ }
+ s.KBsPerSecond = float64(p.state.currentBytes) / 1024.0 / s.SecondsSince
+ s.Description = p.config.description
+ return s
+}
+
+// StartHTTPServer starts an HTTP server dedicated to serving progress bar updates. This allows you to
+// display the status in various UI elements, such as an OS status bar with an `xbar` extension.
+// It is recommended to run this function in a separate goroutine to avoid blocking the main thread.
+//
+// hostPort specifies the address and port to bind the server to, for example, "0.0.0.0:19999".
+func (p *ProgressBar) StartHTTPServer(hostPort string) {
+ // for advanced users, we can return the data as json
+ http.HandleFunc("/state", func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "text/json")
+ // since the state is a simple struct, we can just ignore the error
+ bs, _ := json.Marshal(p.State())
+ w.Write(bs)
+ })
+ // for others, we just return the description in a plain text format
+ http.HandleFunc("/desc", func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "text/plain")
+ fmt.Fprintf(w,
+ "%d/%d, %.2f%%, %s left",
+ p.State().CurrentNum, p.State().Max, p.State().CurrentPercent*100,
+ (time.Second * time.Duration(p.State().SecondsLeft)).String(),
+ )
+ })
+ log.Fatal(http.ListenAndServe(hostPort, nil))
+}
+
+// regex matching ansi escape codes
+var ansiRegex = regexp.MustCompile(`\x1b\[[0-9;]*[a-zA-Z]`)
+
+func getStringWidth(c config, str string, colorize bool) int {
+ if c.colorCodes {
+ // convert any color codes in the progress bar into the respective ANSI codes
+ str = colorstring.Color(str)
+ }
+
+ // the width of the string, if printed to the console
+ // does not include the carriage return character
+ cleanString := strings.Replace(str, "\r", "", -1)
+
+ if c.colorCodes {
+ // the ANSI codes for the colors do not take up space in the console output,
+ // so they do not count towards the output string width
+ cleanString = ansiRegex.ReplaceAllString(cleanString, "")
+ }
+
+ // get the amount of runes in the string instead of the
+ // character count of the string, as some runes span multiple characters.
+ // see https://stackoverflow.com/a/12668840/2733724
+ stringWidth := uniseg.StringWidth(cleanString)
+ return stringWidth
+}
+
+func renderProgressBar(c config, s *state) (int, error) {
+ var sb strings.Builder
+
+ averageRate := average(s.counterLastTenRates)
+ if len(s.counterLastTenRates) == 0 || s.finished {
+ // if no average samples, or if finished,
+ // then average rate should be the total rate
+ if t := time.Since(s.startTime).Seconds(); t > 0 {
+ averageRate = s.currentBytes / t
+ } else {
+ averageRate = 0
+ }
+ }
+
+ // show iteration count in "current/total" iterations format
+ if c.showIterationsCount {
+ if sb.Len() == 0 {
+ sb.WriteString("(")
+ } else {
+ sb.WriteString(", ")
+ }
+ if !c.ignoreLength {
+ if c.showBytes {
+ currentHumanize, currentSuffix := humanizeBytes(s.currentBytes, c.useIECUnits)
+ if currentSuffix == c.maxHumanizedSuffix {
+ if c.showTotalBytes {
+ sb.WriteString(fmt.Sprintf("%s/%s%s",
+ currentHumanize, c.maxHumanized, c.maxHumanizedSuffix))
+ } else {
+ sb.WriteString(fmt.Sprintf("%s%s",
+ currentHumanize, c.maxHumanizedSuffix))
+ }
+ } else if c.showTotalBytes {
+ sb.WriteString(fmt.Sprintf("%s%s/%s%s",
+ currentHumanize, currentSuffix, c.maxHumanized, c.maxHumanizedSuffix))
+ } else {
+ sb.WriteString(fmt.Sprintf("%s%s", currentHumanize, currentSuffix))
+ }
+ } else if c.showTotalBytes {
+ sb.WriteString(fmt.Sprintf("%.0f/%d", s.currentBytes, c.max))
+ } else {
+ sb.WriteString(fmt.Sprintf("%.0f", s.currentBytes))
+ }
+ } else {
+ if c.showBytes {
+ currentHumanize, currentSuffix := humanizeBytes(s.currentBytes, c.useIECUnits)
+ sb.WriteString(fmt.Sprintf("%s%s", currentHumanize, currentSuffix))
+ } else if c.showTotalBytes {
+ sb.WriteString(fmt.Sprintf("%.0f/%s", s.currentBytes, "-"))
+ } else {
+ sb.WriteString(fmt.Sprintf("%.0f", s.currentBytes))
+ }
+ }
+ }
+
+ // show rolling average rate
+ if c.showBytes && averageRate > 0 && !math.IsInf(averageRate, 1) {
+ if sb.Len() == 0 {
+ sb.WriteString("(")
+ } else {
+ sb.WriteString(", ")
+ }
+ currentHumanize, currentSuffix := humanizeBytes(averageRate, c.useIECUnits)
+ sb.WriteString(fmt.Sprintf("%s%s/s", currentHumanize, currentSuffix))
+ }
+
+ // show iterations rate
+ if c.showIterationsPerSecond {
+ if sb.Len() == 0 {
+ sb.WriteString("(")
+ } else {
+ sb.WriteString(", ")
+ }
+ if averageRate > 1 {
+ sb.WriteString(fmt.Sprintf("%0.0f %s/s", averageRate, c.iterationString))
+ } else if averageRate*60 > 1 {
+ sb.WriteString(fmt.Sprintf("%0.0f %s/min", 60*averageRate, c.iterationString))
+ } else {
+ sb.WriteString(fmt.Sprintf("%0.0f %s/hr", 3600*averageRate, c.iterationString))
+ }
+ }
+ if sb.Len() > 0 {
+ sb.WriteString(")")
+ }
+
+ leftBrac, rightBrac, saucer, saucerHead := "", "", "", ""
+ barStart, barEnd := c.theme.BarStart, c.theme.BarEnd
+ if s.finished && c.theme.BarEndFilled != "" {
+ barEnd = c.theme.BarEndFilled
+ }
+
+ // show time prediction in "current/total" seconds format
+ switch {
+ case c.predictTime:
+ rightBracNum := (time.Duration((1/averageRate)*(float64(c.max)-float64(s.currentNum))) * time.Second)
+ if rightBracNum.Seconds() < 0 {
+ rightBracNum = 0 * time.Second
+ }
+ rightBrac = rightBracNum.String()
+ fallthrough
+ case c.elapsedTime || c.showElapsedTimeOnFinish:
+ leftBrac = (time.Duration(time.Since(s.startTime).Seconds()) * time.Second).String()
+ }
+
+ if c.fullWidth && !c.ignoreLength {
+ width, err := termWidth()
+ if err != nil {
+ width = 80
+ }
+
+ amend := 1 // an extra space at eol
+ switch {
+ case leftBrac != "" && rightBrac != "":
+ amend = 4 // space, square brackets and colon
+ case leftBrac != "" && rightBrac == "":
+ amend = 4 // space and square brackets and another space
+ case leftBrac == "" && rightBrac != "":
+ amend = 3 // space and square brackets
+ }
+ if c.showDescriptionAtLineEnd {
+ amend += 1 // another space
+ }
+
+ c.width = width - getStringWidth(c, c.description, true) - 10 - amend - sb.Len() - len(leftBrac) - len(rightBrac)
+ s.currentSaucerSize = int(float64(s.currentPercent) / 100.0 * float64(c.width))
+ }
+ if (s.currentSaucerSize > 0 || s.currentPercent > 0) && c.theme.BarStartFilled != "" {
+ barStart = c.theme.BarStartFilled
+ }
+ if s.currentSaucerSize > 0 {
+ if c.ignoreLength {
+ saucer = strings.Repeat(c.theme.SaucerPadding, s.currentSaucerSize-1)
+ } else {
+ saucer = strings.Repeat(c.theme.Saucer, s.currentSaucerSize-1)
+ }
+
+ // Check if an alternate saucer head is set for animation
+ if c.theme.AltSaucerHead != "" && s.isAltSaucerHead {
+ saucerHead = c.theme.AltSaucerHead
+ s.isAltSaucerHead = false
+ } else if c.theme.SaucerHead == "" || s.currentSaucerSize == c.width {
+ // use the saucer for the saucer head if it hasn't been set
+ // to preserve backwards compatibility
+ saucerHead = c.theme.Saucer
+ } else {
+ saucerHead = c.theme.SaucerHead
+ s.isAltSaucerHead = true
+ }
+ }
+
+ /*
+ Progress Bar format
+ Description % |------ | (kb/s) (iteration count) (iteration rate) (predict time)
+
+ or if showDescriptionAtLineEnd is enabled
+ % |------ | (kb/s) (iteration count) (iteration rate) (predict time) Description
+ */
+
+ repeatAmount := c.width - s.currentSaucerSize
+ if repeatAmount < 0 {
+ repeatAmount = 0
+ }
+
+ str := ""
+
+ if c.ignoreLength {
+ selectedSpinner := spinners[c.spinnerType]
+ if len(c.spinner) > 0 {
+ selectedSpinner = c.spinner
+ }
+
+ var spinner string
+ if c.spinnerChangeInterval != 0 {
+ // if the spinner is changed according to an interval, calculate it
+ spinner = selectedSpinner[int(math.Round(math.Mod(float64(time.Since(s.startTime).Nanoseconds()/c.spinnerChangeInterval.Nanoseconds()), float64(len(selectedSpinner)))))]
+ } else {
+ // if the spinner is changed according to the number render was called
+ spinner = selectedSpinner[s.spinnerIdx]
+ s.spinnerIdx = (s.spinnerIdx + 1) % len(selectedSpinner)
+ }
+ if c.elapsedTime {
+ if c.showDescriptionAtLineEnd {
+ str = fmt.Sprintf("\r%s %s [%s] %s ",
+ spinner,
+ sb.String(),
+ leftBrac,
+ c.description)
+ } else {
+ str = fmt.Sprintf("\r%s %s %s [%s] ",
+ spinner,
+ c.description,
+ sb.String(),
+ leftBrac)
+ }
+ } else {
+ if c.showDescriptionAtLineEnd {
+ str = fmt.Sprintf("\r%s %s %s ",
+ spinner,
+ sb.String(),
+ c.description)
+ } else {
+ str = fmt.Sprintf("\r%s %s %s ",
+ spinner,
+ c.description,
+ sb.String())
+ }
+ }
+ } else if rightBrac == "" {
+ str = fmt.Sprintf("%4d%% %s%s%s%s%s %s",
+ s.currentPercent,
+ barStart,
+ saucer,
+ saucerHead,
+ strings.Repeat(c.theme.SaucerPadding, repeatAmount),
+ barEnd,
+ sb.String())
+ if (s.currentPercent == 100 && c.showElapsedTimeOnFinish) || c.elapsedTime {
+ str = fmt.Sprintf("%s [%s]", str, leftBrac)
+ }
+
+ if c.showDescriptionAtLineEnd {
+ str = fmt.Sprintf("\r%s %s ", str, c.description)
+ } else {
+ str = fmt.Sprintf("\r%s%s ", c.description, str)
+ }
+ } else {
+ if s.currentPercent == 100 {
+ str = fmt.Sprintf("%4d%% %s%s%s%s%s %s",
+ s.currentPercent,
+ barStart,
+ saucer,
+ saucerHead,
+ strings.Repeat(c.theme.SaucerPadding, repeatAmount),
+ barEnd,
+ sb.String())
+
+ if c.showElapsedTimeOnFinish {
+ str = fmt.Sprintf("%s [%s]", str, leftBrac)
+ }
+
+ if c.showDescriptionAtLineEnd {
+ str = fmt.Sprintf("\r%s %s", str, c.description)
+ } else {
+ str = fmt.Sprintf("\r%s%s", c.description, str)
+ }
+ } else {
+ str = fmt.Sprintf("%4d%% %s%s%s%s%s %s [%s:%s]",
+ s.currentPercent,
+ barStart,
+ saucer,
+ saucerHead,
+ strings.Repeat(c.theme.SaucerPadding, repeatAmount),
+ barEnd,
+ sb.String(),
+ leftBrac,
+ rightBrac)
+
+ if c.showDescriptionAtLineEnd {
+ str = fmt.Sprintf("\r%s %s", str, c.description)
+ } else {
+ str = fmt.Sprintf("\r%s%s", c.description, str)
+ }
+ }
+ }
+
+ if c.colorCodes {
+ // convert any color codes in the progress bar into the respective ANSI codes
+ str = colorstring.Color(str)
+ }
+
+ s.rendered = str
+
+ return getStringWidth(c, str, false), writeString(c, str)
+}
+
+func clearProgressBar(c config, s state) error {
+ if s.maxLineWidth == 0 {
+ return nil
+ }
+ if c.useANSICodes {
+ // write the "clear current line" ANSI escape sequence
+ return writeString(c, "\033[2K\r")
+ }
+ // fill the empty content
+ // to overwrite the progress bar and jump
+ // back to the beginning of the line
+ str := fmt.Sprintf("\r%s\r", strings.Repeat(" ", s.maxLineWidth))
+ return writeString(c, str)
+ // the following does not show correctly if the previous line is longer than subsequent line
+ // return writeString(c, "\r")
+}
+
+func writeString(c config, str string) error {
+ if _, err := io.WriteString(c.writer, str); err != nil {
+ return err
+ }
+
+ if f, ok := c.writer.(*os.File); ok {
+ // ignore any errors in Sync(), as stdout
+ // can't be synced on some operating systems
+ // like Debian 9 (Stretch)
+ f.Sync()
+ }
+
+ return nil
+}
+
+// Reader is the progressbar io.Reader struct
+type Reader struct {
+ io.Reader
+ bar *ProgressBar
+}
+
+// NewReader return a new Reader with a given progress bar.
+func NewReader(r io.Reader, bar *ProgressBar) Reader {
+ return Reader{
+ Reader: r,
+ bar: bar,
+ }
+}
+
+// Read will read the data and add the number of bytes to the progressbar
+func (r *Reader) Read(p []byte) (n int, err error) {
+ n, err = r.Reader.Read(p)
+ r.bar.Add(n)
+ return
+}
+
+// Close the reader when it implements io.Closer
+func (r *Reader) Close() (err error) {
+ if closer, ok := r.Reader.(io.Closer); ok {
+ return closer.Close()
+ }
+ r.bar.Finish()
+ return
+}
+
+// Write implement io.Writer
+func (p *ProgressBar) Write(b []byte) (n int, err error) {
+ n = len(b)
+ err = p.Add(n)
+ return
+}
+
+// Read implement io.Reader
+func (p *ProgressBar) Read(b []byte) (n int, err error) {
+ n = len(b)
+ err = p.Add(n)
+ return
+}
+
+func (p *ProgressBar) Close() (err error) {
+ err = p.Finish()
+ return
+}
+
+func average(xs []float64) float64 {
+ total := 0.0
+ for _, v := range xs {
+ total += v
+ }
+ return total / float64(len(xs))
+}
+
+func humanizeBytes(s float64, iec bool) (string, string) {
+ sizes := []string{" B", " kB", " MB", " GB", " TB", " PB", " EB"}
+ base := 1000.0
+
+ if iec {
+ sizes = []string{" B", " KiB", " MiB", " GiB", " TiB", " PiB", " EiB"}
+ base = 1024.0
+ }
+
+ if s < 10 {
+ return fmt.Sprintf("%2.0f", s), sizes[0]
+ }
+ e := math.Floor(logn(float64(s), base))
+ suffix := sizes[int(e)]
+ val := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10
+ f := "%.0f"
+ if val < 10 {
+ f = "%.1f"
+ }
+
+ return fmt.Sprintf(f, val), suffix
+}
+
+func logn(n, b float64) float64 {
+ return math.Log(n) / math.Log(b)
+}
+
+// termWidth function returns the visible width of the current terminal
+// and can be redefined for testing
+var termWidth = func() (width int, err error) {
+ width, _, err = term.GetSize(int(os.Stdout.Fd()))
+ if err == nil {
+ return width, nil
+ }
+
+ return 0, err
+}
+
+func shouldCacheOutput(pb *ProgressBar) bool {
+ return !pb.state.finished && !pb.state.exit && !pb.config.invisible
+}
+
+func Bprintln(pb *ProgressBar, a ...interface{}) (int, error) {
+ pb.lock.Lock()
+ defer pb.lock.Unlock()
+ if !shouldCacheOutput(pb) {
+ return fmt.Fprintln(pb.config.writer, a...)
+ } else {
+ return fmt.Fprintln(&pb.config.stdBuffer, a...)
+ }
+}
+
+func Bprintf(pb *ProgressBar, format string, a ...interface{}) (int, error) {
+ pb.lock.Lock()
+ defer pb.lock.Unlock()
+ if !shouldCacheOutput(pb) {
+ return fmt.Fprintf(pb.config.writer, format, a...)
+ } else {
+ return fmt.Fprintf(&pb.config.stdBuffer, format, a...)
+ }
+}
diff --git a/vendor/github.com/schollz/progressbar/v3/spinners.go b/vendor/github.com/schollz/progressbar/v3/spinners.go
new file mode 100644
index 0000000..c3ccd01
--- /dev/null
+++ b/vendor/github.com/schollz/progressbar/v3/spinners.go
@@ -0,0 +1,80 @@
+package progressbar
+
+var spinners = map[int][]string{
+ 0: {"←", "↖", "↑", "↗", "→", "↘", "↓", "↙"},
+ 1: {"▁", "▃", "▄", "▅", "▆", "▇", "█", "▇", "▆", "▅", "▄", "▃", "▁"},
+ 2: {"▖", "▘", "▝", "▗"},
+ 3: {"┤", "┘", "┴", "└", "├", "┌", "┬", "┐"},
+ 4: {"◢", "◣", "◤", "◥"},
+ 5: {"◰", "◳", "◲", "◱"},
+ 6: {"◴", "◷", "◶", "◵"},
+ 7: {"◐", "◓", "◑", "◒"},
+ 8: {".", "o", "O", "@", "*"},
+ 9: {"|", "/", "-", "\\"},
+ 10: {"◡◡", "⊙⊙", "◠◠"},
+ 11: {"⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"},
+ 12: {">))'>", " >))'>", " >))'>", " >))'>", " >))'>", " <'((<", " <'((<", " <'((<"},
+ 13: {"⠁", "⠂", "⠄", "⡀", "⢀", "⠠", "⠐", "⠈"},
+ 14: {"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"},
+ 15: {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"},
+ 16: {"▉", "▊", "▋", "▌", "▍", "▎", "▏", "▎", "▍", "▌", "▋", "▊", "▉"},
+ 17: {"■", "□", "▪", "▫"},
+ 18: {"←", "↑", "→", "↓"},
+ 19: {"╫", "╪"},
+ 20: {"⇐", "⇖", "⇑", "⇗", "⇒", "⇘", "⇓", "⇙"},
+ 21: {"⠁", "⠁", "⠉", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠤", "⠄", "⠄", "⠤", "⠠", "⠠", "⠤", "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋", "⠉", "⠈", "⠈"},
+ 22: {"⠈", "⠉", "⠋", "⠓", "⠒", "⠐", "⠐", "⠒", "⠖", "⠦", "⠤", "⠠", "⠠", "⠤", "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋", "⠉", "⠈"},
+ 23: {"⠁", "⠉", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠤", "⠄", "⠄", "⠤", "⠴", "⠲", "⠒", "⠂", "⠂", "⠒", "⠚", "⠙", "⠉", "⠁"},
+ 24: {"⠋", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋"},
+ 25: {"ヲ", "ァ", "ィ", "ゥ", "ェ", "ォ", "ャ", "ュ", "ョ", "ッ", "ア", "イ", "ウ", "エ", "オ", "カ", "キ", "ク", "ケ", "コ", "サ", "シ", "ス", "セ", "ソ", "タ", "チ", "ツ", "テ", "ト", "ナ", "ニ", "ヌ", "ネ", "ノ", "ハ", "ヒ", "フ", "ヘ", "ホ", "マ", "ミ", "ム", "メ", "モ", "ヤ", "ユ", "ヨ", "ラ", "リ", "ル", "レ", "ロ", "ワ", "ン"},
+ 26: {".", "..", "..."},
+ 27: {"▁", "▂", "▃", "▄", "▅", "▆", "▇", "█", "▉", "▊", "▋", "▌", "▍", "▎", "▏", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█", "▇", "▆", "▅", "▄", "▃", "▂", "▁"},
+ 28: {".", "o", "O", "°", "O", "o", "."},
+ 29: {"+", "x"},
+ 30: {"v", "<", "^", ">"},
+ 31: {">>--->", " >>--->", " >>--->", " >>--->", " >>--->", " <---<<", " <---<<", " <---<<", " <---<<", "<---<<"},
+ 32: {"|", "||", "|||", "||||", "|||||", "|||||||", "||||||||", "|||||||", "||||||", "|||||", "||||", "|||", "||", "|"},
+ 33: {"[ ]", "[= ]", "[== ]", "[=== ]", "[==== ]", "[===== ]", "[====== ]", "[======= ]", "[======== ]", "[========= ]", "[==========]"},
+ 34: {"(*---------)", "(-*--------)", "(--*-------)", "(---*------)", "(----*-----)", "(-----*----)", "(------*---)", "(-------*--)", "(--------*-)", "(---------*)"},
+ 35: {"█▒▒▒▒▒▒▒▒▒", "███▒▒▒▒▒▒▒", "█████▒▒▒▒▒", "███████▒▒▒", "██████████"},
+ 36: {"[ ]", "[=> ]", "[===> ]", "[=====> ]", "[======> ]", "[========> ]", "[==========> ]", "[============> ]", "[==============> ]", "[================> ]", "[==================> ]", "[===================>]"},
+ 37: {"ဝ", "၀"},
+ 38: {"▌", "▀", "▐▄"},
+ 39: {"🌍", "🌎", "🌏"},
+ 40: {"◜", "◝", "◞", "◟"},
+ 41: {"⬒", "⬔", "⬓", "⬕"},
+ 42: {"⬖", "⬘", "⬗", "⬙"},
+ 43: {"[>>> >]", "[]>>>> []", "[] >>>> []", "[] >>>> []", "[] >>>> []", "[] >>>>[]", "[>> >>]"},
+ 44: {"♠", "♣", "♥", "♦"},
+ 45: {"➞", "➟", "➠", "➡", "➠", "➟"},
+ 46: {" | ", ` \ `, "_ ", ` \ `, " | ", " / ", " _", " / "},
+ 47: {" . . . .", ". . . .", ". . . .", ". . . .", ". . . . ", ". . . . ."},
+ 48: {" | ", " / ", " _ ", ` \ `, " | ", ` \ `, " _ ", " / "},
+ 49: {"⎺", "⎻", "⎼", "⎽", "⎼", "⎻"},
+ 50: {"▹▹▹▹▹", "▸▹▹▹▹", "▹▸▹▹▹", "▹▹▸▹▹", "▹▹▹▸▹", "▹▹▹▹▸"},
+ 51: {"[ ]", "[ =]", "[ ==]", "[ ===]", "[====]", "[=== ]", "[== ]", "[= ]"},
+ 52: {"( ● )", "( ● )", "( ● )", "( ● )", "( ●)", "( ● )", "( ● )", "( ● )", "( ● )"},
+ 53: {"✶", "✸", "✹", "✺", "✹", "✷"},
+ 54: {"▐|\\____________▌", "▐_|\\___________▌", "▐__|\\__________▌", "▐___|\\_________▌", "▐____|\\________▌", "▐_____|\\_______▌", "▐______|\\______▌", "▐_______|\\_____▌", "▐________|\\____▌", "▐_________|\\___▌", "▐__________|\\__▌", "▐___________|\\_▌", "▐____________|\\▌", "▐____________/|▌", "▐___________/|_▌", "▐__________/|__▌", "▐_________/|___▌", "▐________/|____▌", "▐_______/|_____▌", "▐______/|______▌", "▐_____/|_______▌", "▐____/|________▌", "▐___/|_________▌", "▐__/|__________▌", "▐_/|___________▌", "▐/|____________▌"},
+ 55: {"▐⠂ ▌", "▐⠈ ▌", "▐ ⠂ ▌", "▐ ⠠ ▌", "▐ ⡀ ▌", "▐ ⠠ ▌", "▐ ⠂ ▌", "▐ ⠈ ▌", "▐ ⠂ ▌", "▐ ⠠ ▌", "▐ ⡀ ▌", "▐ ⠠ ▌", "▐ ⠂ ▌", "▐ ⠈ ▌", "▐ ⠂▌", "▐ ⠠▌", "▐ ⡀▌", "▐ ⠠ ▌", "▐ ⠂ ▌", "▐ ⠈ ▌", "▐ ⠂ ▌", "▐ ⠠ ▌", "▐ ⡀ ▌", "▐ ⠠ ▌", "▐ ⠂ ▌", "▐ ⠈ ▌", "▐ ⠂ ▌", "▐ ⠠ ▌", "▐ ⡀ ▌", "▐⠠ ▌"},
+ 56: {"¿", "?"},
+ 57: {"⢹", "⢺", "⢼", "⣸", "⣇", "⡧", "⡗", "⡏"},
+ 58: {"⢄", "⢂", "⢁", "⡁", "⡈", "⡐", "⡠"},
+ 59: {". ", ".. ", "...", " ..", " .", " "},
+ 60: {".", "o", "O", "°", "O", "o", "."},
+ 61: {"▓", "▒", "░"},
+ 62: {"▌", "▀", "▐", "▄"},
+ 63: {"⊶", "⊷"},
+ 64: {"▪", "▫"},
+ 65: {"□", "■"},
+ 66: {"▮", "▯"},
+ 67: {"-", "=", "≡"},
+ 68: {"d", "q", "p", "b"},
+ 69: {"∙∙∙", "●∙∙", "∙●∙", "∙∙●", "∙∙∙"},
+ 70: {"🌑 ", "🌒 ", "🌓 ", "🌔 ", "🌕 ", "🌖 ", "🌗 ", "🌘 "},
+ 71: {"☗", "☖"},
+ 72: {"⧇", "⧆"},
+ 73: {"◉", "◎"},
+ 74: {"㊂", "㊀", "㊁"},
+ 75: {"⦾", "⦿"},
+}