diff options
Diffstat (limited to 'vendor/github.com/schollz')
| -rw-r--r-- | vendor/github.com/schollz/progressbar/v3/.gitignore | 18 | ||||
| -rw-r--r-- | vendor/github.com/schollz/progressbar/v3/.golangci.yml | 21 | ||||
| -rw-r--r-- | vendor/github.com/schollz/progressbar/v3/.travis.yml | 6 | ||||
| -rw-r--r-- | vendor/github.com/schollz/progressbar/v3/LICENSE | 21 | ||||
| -rw-r--r-- | vendor/github.com/schollz/progressbar/v3/README.md | 121 | ||||
| -rw-r--r-- | vendor/github.com/schollz/progressbar/v3/progressbar.go | 1490 | ||||
| -rw-r--r-- | vendor/github.com/schollz/progressbar/v3/spinners.go | 80 |
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 + +[](https://github.com/schollz/progressbar/actions/workflows/ci.yml) +[](https://goreportcard.com/report/github.com/schollz/progressbar) +[](https://gocover.io/github.com/schollz/progressbar) +[](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: + + + + +### 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: + + + + +### 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: + + + + +### 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: + + + + +## 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: {"⦾", "⦿"}, +} |
