[minor] show a pretty progress bar when follow actions in clients

This commit is contained in:
Julien Vehent 2016-01-14 15:25:36 -05:00
Родитель bde41c6d4a
Коммит 8541f1a2d6
24 изменённых файлов: 1187 добавлений и 33 удалений

Просмотреть файл

@ -121,6 +121,7 @@ go_vendor_dependencies:
$(GOGETTER) golang.org/x/net/ipv4
$(GOGETTER) golang.org/x/net/ipv6
$(GOGETTER) gopkg.in/gcfg.v1
$(GOGETTER) github.com/cheggaaa/pb
echo 'removing .git from vendored pkg and moving them to vendor'
find .tmpdeps/src -type d -name ".git" ! -name ".gitignore" -exec rm -rf {} \; || exit 0
cp -ar .tmpdeps/src/* vendor/

Просмотреть файл

@ -23,6 +23,7 @@ import (
"strings"
"time"
"github.com/cheggaaa/pb"
"github.com/jvehent/cljs"
"golang.org/x/crypto/openpgp"
"gopkg.in/gcfg.v1"
@ -722,24 +723,27 @@ func (cli Client) EvaluateAgentTarget(target string) (agents []mig.Agent, err er
// FollowAction continuously loops over an action and prints its completion status in os.Stderr.
// when the action reaches its expiration date, FollowAction prints its final status and returns.
func (cli Client) FollowAction(a mig.Action) (err error) {
func (cli Client) FollowAction(a mig.Action, total int) (err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("followAction() -> %v", e)
}
}()
fmt.Fprintf(os.Stderr, "\x1b[34mFollowing action ID %.0f.\x1b[0m", a.ID)
sent := 0
dotter := 0
fmt.Fprintf(os.Stderr, "\x1b[34mFollowing action ID %.0f.\x1b[0m\n", a.ID)
previousctr := 0
status := ""
attempts := 0
var completion float64
bar := pb.New(total)
bar.ShowSpeed = true
bar.SetMaxWidth(80)
bar.Output = os.Stderr
bar.Start()
for {
a, _, err = cli.GetAction(a.ID)
if err != nil {
attempts++
time.Sleep(1 * time.Second)
time.Sleep(time.Second)
if attempts >= 30 {
panic("failed to retrieve action after 30 seconds. launch may have failed")
}
@ -748,10 +752,6 @@ func (cli Client) FollowAction(a mig.Action) (err error) {
if status == "" {
status = a.Status
}
if status != a.Status {
fmt.Fprintf(os.Stderr, "\x1b[34mstatus=%s\x1b[0m", a.Status)
status = a.Status
}
// exit follower mode if status isn't one we follow,
// or enough commands have returned
// or expiration time has passed
@ -761,33 +761,26 @@ func (cli Client) FollowAction(a mig.Action) (err error) {
goto finish
break
}
// init counters
if sent == 0 {
if a.Counters.Sent == 0 {
time.Sleep(1 * time.Second)
continue
} else {
sent = a.Counters.Sent
}
}
if a.Counters.Done > 0 && a.Counters.Done > previousctr {
completion = (float64(a.Counters.Done) / float64(a.Counters.Sent)) * 100
if completion < 99.5 {
bar.Add(a.Counters.Done - previousctr)
bar.Update()
previousctr = a.Counters.Done
fmt.Fprintf(os.Stderr, "\x1b[34m%.0f%%\x1b[0m", completion)
}
}
fmt.Fprintf(os.Stderr, "\x1b[34m.\x1b[0m")
time.Sleep(2 * time.Second)
dotter++
}
finish:
bar.Add(total - previousctr)
bar.Update()
bar.Finish()
a, _, err = cli.GetAction(a.ID)
if err != nil {
fmt.Fprintf(os.Stderr, "[error] failed to retrieve action counters\n")
} else {
completion = (float64(a.Counters.Done) / float64(a.Counters.Sent)) * 100
fmt.Fprintf(os.Stderr, "\n\x1b[34m- %2.1f%% done in %s\x1b[0m\n", completion, time.Now().Sub(a.StartTime).String())
fmt.Fprintf(os.Stderr, "\x1b[34m%2.1f%% done in %s\x1b[0m\n", completion, time.Now().Sub(a.StartTime).String())
}
fmt.Fprintf(os.Stderr, "\x1b[34m")
a.PrintCounters()

Просмотреть файл

@ -8,14 +8,15 @@ package main
import (
"encoding/json"
"fmt"
"github.com/bobappleyard/readline"
"io"
"mig.ninja/mig"
"mig.ninja/mig/client"
"mig.ninja/mig/modules"
"strconv"
"strings"
"time"
"github.com/bobappleyard/readline"
"mig.ninja/mig"
"mig.ninja/mig/client"
"mig.ninja/mig/modules"
)
// default expiration is 300 seconds
@ -29,7 +30,10 @@ func actionLauncher(tpl mig.Action, cli client.Client) (err error) {
err = fmt.Errorf("actionLauncher() -> %v", e)
}
}()
var a mig.Action
var (
a mig.Action
tcount int
)
if tpl.ID == 0 {
fmt.Println("Entering action launcher with empty template")
} else {
@ -188,12 +192,12 @@ times show the various timestamps of the action
if err != nil {
panic(err)
}
count := len(agents)
if count == 0 {
tcount = len(agents)
if tcount == 0 {
fmt.Println("0 agents match this target. launch aborted")
break
}
fmt.Printf("%d agents will be targeted by search \"%s\"\n", count, a.Target)
fmt.Printf("%d agents will be targeted by search \"%s\"\n", tcount, a.Target)
input, err = readline.String("continue? (y/n)> ")
if err != nil {
panic(err)
@ -230,7 +234,7 @@ times show the various timestamps of the action
fmt.Printf("Action '%s' successfully launched with ID '%.0f' on target '%s'\n",
a.Name, a.ID, a.Target)
if follow {
err = cli.FollowAction(a)
err = cli.FollowAction(a, tcount)
if err != nil {
panic(err)
}
@ -286,7 +290,8 @@ times show the various timestamps of the action
fmt.Println(err)
break
}
fmt.Printf("%d agents will be targetted. To get the list, use 'listagents'\n", len(agents))
tcount = len(agents)
fmt.Printf("%d agents will be targetted. To get the list, use 'listagents'\n", tcount)
hasEvaluatedTarget = true
case "settimes":
// set the dates

Просмотреть файл

@ -279,7 +279,7 @@ readytolaunch:
done := make(chan bool, 1)
signal.Notify(c, os.Interrupt)
go func() {
err = cli.FollowAction(a)
err = cli.FollowAction(a, len(agents))
if err != nil {
panic(err)
}

4
vendor/github.com/cheggaaa/pb/.travis.yml сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,4 @@
language: go
go:
- 1.4.2
sudo: false

12
vendor/github.com/cheggaaa/pb/LICENSE сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,12 @@
Copyright (c) 2012-2015, Sergey Cherepanov
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

173
vendor/github.com/cheggaaa/pb/README.md сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,173 @@
# Terminal progress bar for Go
Simple progress bar for console programs.
## Installation
```
go get github.com/cheggaaa/pb
```
## Usage
```Go
package main
import (
"github.com/cheggaaa/pb"
"time"
)
func main() {
count := 100000
bar := pb.StartNew(count)
for i := 0; i < count; i++ {
bar.Increment()
time.Sleep(time.Millisecond)
}
bar.FinishPrint("The End!")
}
```
Result will be like this:
```
> go run test.go
37158 / 100000 [================>_______________________________] 37.16% 1m11s
```
## Customization
```Go
// create bar
bar := pb.New(count)
// refresh info every second (default 200ms)
bar.SetRefreshRate(time.Second)
// show percents (by default already true)
bar.ShowPercent = true
// show bar (by default already true)
bar.ShowBar = true
// no counters
bar.ShowCounters = false
// show "time left"
bar.ShowTimeLeft = true
// show average speed
bar.ShowSpeed = true
// sets the width of the progress bar
bar.SetWidth(80)
// sets the width of the progress bar, but if terminal size smaller will be ignored
bar.SetMaxWidth(80)
// convert output to readable format (like KB, MB)
bar.SetUnits(pb.U_BYTES)
// and start
bar.Start()
```
## Progress bar for IO Operations
```go
// create and start bar
bar := pb.New(myDataLen).SetUnits(pb.U_BYTES)
bar.Start()
// my io.Reader
r := myReader
// my io.Writer
w := myWriter
// create proxy reader
reader := bar.NewProxyReader(r)
// and copy from pb reader
io.Copy(w, reader)
```
```go
// create and start bar
bar := pb.New(myDataLen).SetUnits(pb.U_BYTES)
bar.Start()
// my io.Reader
r := myReader
// my io.Writer
w := myWriter
// create multi writer
writer := io.MultiWriter(w, bar)
// and copy
io.Copy(writer, r)
```
## Custom Progress Bar Look-and-feel
```go
bar.Format("<.- >")
```
## Multiple Progress Bars (experimental and unstable)
#####Multiple bars do not works at Windows!!!
Do not print to terminal while pool is active.
```go
package main
import (
"math/rand"
"github.com/cheggaaa/pb"
"sync"
"time"
)
// create bars
first := pb.New(200).Prefix("First ")
second := pb.New(200).Prefix("Second ")
third := pb.New(200).Prefix("Third ")
// start pool
pool, err := pb.StartPool(first, second, third)
if err != nil {
panic(err)
}
// update bars
wg := new(sync.WaitGroup)
for _, bar := range []*pb.ProgressBar{first, second, third} {
wg.Add(1)
go func(cb *pb.ProgressBar) {
for n := 0; n < 200; n++ {
cb.Increment()
time.Sleep(time.Millisecond * time.Duration(rand.Intn(100)))
}
cb.Finish()
wg.Done()
}(bar)
}
wg.Wait()
// close pool
pool.Stop()
```
The result will be as follows:
```
$ go run example/multiple.go
First 141 / 1000 [===============>---------------------------------------] 14.10 % 44s
Second 139 / 1000 [==============>---------------------------------------] 13.90 % 44s
Third 152 / 1000 [================>--------------------------------------] 15.20 % 40s
```

82
vendor/github.com/cheggaaa/pb/example_copy_test.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,82 @@
package pb_test
import (
"fmt"
"io"
"net/http"
"os"
"strconv"
"strings"
"time"
"github.com/cheggaaa/pb"
)
func Example_copy() {
// check args
if len(os.Args) < 3 {
printUsage()
return
}
sourceName, destName := os.Args[1], os.Args[2]
// check source
var source io.Reader
var sourceSize int64
if strings.HasPrefix(sourceName, "http://") {
// open as url
resp, err := http.Get(sourceName)
if err != nil {
fmt.Printf("Can't get %s: %v\n", sourceName, err)
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
fmt.Printf("Server return non-200 status: %v\n", resp.Status)
return
}
i, _ := strconv.Atoi(resp.Header.Get("Content-Length"))
sourceSize = int64(i)
source = resp.Body
} else {
// open as file
s, err := os.Open(sourceName)
if err != nil {
fmt.Printf("Can't open %s: %v\n", sourceName, err)
return
}
defer s.Close()
// get source size
sourceStat, err := s.Stat()
if err != nil {
fmt.Printf("Can't stat %s: %v\n", sourceName, err)
return
}
sourceSize = sourceStat.Size()
source = s
}
// create dest
dest, err := os.Create(destName)
if err != nil {
fmt.Printf("Can't create %s: %v\n", destName, err)
return
}
defer dest.Close()
// create bar
bar := pb.New(int(sourceSize)).SetUnits(pb.U_BYTES).SetRefreshRate(time.Millisecond * 10)
bar.ShowSpeed = true
bar.Start()
// create proxy reader
reader := bar.NewProxyReader(source)
// and copy from reader
io.Copy(dest, reader)
bar.Finish()
}
func printUsage() {
fmt.Println("copy [source file or url] [dest file]")
}

37
vendor/github.com/cheggaaa/pb/example_multiple_test.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,37 @@
package pb_test
import (
"math/rand"
"sync"
"time"
"github.com/cheggaaa/pb"
)
func Example_multiple() {
// create bars
first := pb.New(200).Prefix("First ")
second := pb.New(200).Prefix("Second ")
third := pb.New(200).Prefix("Third ")
// start pool
pool, err := pb.StartPool(first, second, third)
if err != nil {
panic(err)
}
// update bars
wg := new(sync.WaitGroup)
for _, bar := range []*pb.ProgressBar{first, second, third} {
wg.Add(1)
go func(cb *pb.ProgressBar) {
for n := 0; n < 200; n++ {
cb.Increment()
time.Sleep(time.Millisecond * time.Duration(rand.Intn(100)))
}
cb.Finish()
wg.Done()
}(bar)
}
wg.Wait()
// close pool
pool.Stop()
}

30
vendor/github.com/cheggaaa/pb/example_test.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,30 @@
package pb_test
import (
"time"
"github.com/cheggaaa/pb"
)
func Example() {
count := 5000
bar := pb.New(count)
// show percents (by default already true)
bar.ShowPercent = true
// show bar (by default already true)
bar.ShowBar = true
bar.ShowCounters = true
bar.ShowTimeLeft = true
// and start
bar.Start()
for i := 0; i < count; i++ {
bar.Increment()
time.Sleep(time.Millisecond)
}
bar.FinishPrint("The End!")
}

45
vendor/github.com/cheggaaa/pb/format.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,45 @@
package pb
import (
"fmt"
"strconv"
"strings"
)
type Units int
const (
// By default, without type handle
U_NO Units = iota
// Handle as b, Kb, Mb, etc
U_BYTES
)
// Format integer
func Format(i int64, units Units) string {
switch units {
case U_BYTES:
return FormatBytes(i)
default:
// by default just convert to string
return strconv.FormatInt(i, 10)
}
}
// Convert bytes to human readable string. Like a 2 MB, 64.2 KB, 52 B
func FormatBytes(i int64) (result string) {
switch {
case i > (1024 * 1024 * 1024 * 1024):
result = fmt.Sprintf("%.02f TB", float64(i)/1024/1024/1024/1024)
case i > (1024 * 1024 * 1024):
result = fmt.Sprintf("%.02f GB", float64(i)/1024/1024/1024)
case i > (1024 * 1024):
result = fmt.Sprintf("%.02f MB", float64(i)/1024/1024)
case i > 1024:
result = fmt.Sprintf("%.02f KB", float64(i)/1024)
default:
result = fmt.Sprintf("%d B", i)
}
result = strings.Trim(result, " ")
return
}

37
vendor/github.com/cheggaaa/pb/format_test.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,37 @@
package pb
import (
"fmt"
"strconv"
"testing"
)
func Test_DefaultsToInteger(t *testing.T) {
value := int64(1000)
expected := strconv.Itoa(int(value))
actual := Format(value, -1)
if actual != expected {
t.Error(fmt.Sprintf("Expected {%s} was {%s}", expected, actual))
}
}
func Test_CanFormatAsInteger(t *testing.T) {
value := int64(1000)
expected := strconv.Itoa(int(value))
actual := Format(value, U_NO)
if actual != expected {
t.Error(fmt.Sprintf("Expected {%s} was {%s}", expected, actual))
}
}
func Test_CanFormatAsBytes(t *testing.T) {
value := int64(1000)
expected := "1000 B"
actual := Format(value, U_BYTES)
if actual != expected {
t.Error(fmt.Sprintf("Expected {%s} was {%s}", expected, actual))
}
}

385
vendor/github.com/cheggaaa/pb/pb.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,385 @@
// Simple console progress bars
package pb
import (
"fmt"
"io"
"math"
"strings"
"sync"
"sync/atomic"
"time"
"unicode/utf8"
)
const (
// Default refresh rate - 200ms
DEFAULT_REFRESH_RATE = time.Millisecond * 200
FORMAT = "[=>-]"
)
// DEPRECATED
// variables for backward compatibility, from now do not work
// use pb.Format and pb.SetRefreshRate
var (
DefaultRefreshRate = DEFAULT_REFRESH_RATE
BarStart, BarEnd, Empty, Current, CurrentN string
)
// Create new progress bar object
func New(total int) *ProgressBar {
return New64(int64(total))
}
// Create new progress bar object uding int64 as total
func New64(total int64) *ProgressBar {
pb := &ProgressBar{
Total: total,
RefreshRate: DEFAULT_REFRESH_RATE,
ShowPercent: true,
ShowCounters: true,
ShowBar: true,
ShowTimeLeft: true,
ShowFinalTime: true,
Units: U_NO,
ManualUpdate: false,
finish: make(chan struct{}),
currentValue: -1,
}
return pb.Format(FORMAT)
}
// Create new object and start
func StartNew(total int) *ProgressBar {
return New(total).Start()
}
// Callback for custom output
// For example:
// bar.Callback = func(s string) {
// mySuperPrint(s)
// }
//
type Callback func(out string)
type ProgressBar struct {
current int64 // current must be first member of struct (https://code.google.com/p/go/issues/detail?id=5278)
Total int64
RefreshRate time.Duration
ShowPercent, ShowCounters bool
ShowSpeed, ShowTimeLeft, ShowBar bool
ShowFinalTime bool
Output io.Writer
Callback Callback
NotPrint bool
Units Units
Width int
ForceWidth bool
ManualUpdate bool
finishOnce sync.Once //Guards isFinish
finish chan struct{}
isFinish bool
startTime time.Time
startValue int64
currentValue int64
prefix, postfix string
lastPrint string
BarStart string
BarEnd string
Empty string
Current string
CurrentN string
AlwaysUpdate bool
}
// Start print
func (pb *ProgressBar) Start() *ProgressBar {
pb.startTime = time.Now()
pb.startValue = pb.current
if pb.Total == 0 {
pb.ShowTimeLeft = false
pb.ShowPercent = false
}
if !pb.ManualUpdate {
go pb.writer()
}
return pb
}
// Increment current value
func (pb *ProgressBar) Increment() int {
return pb.Add(1)
}
// Set current value
func (pb *ProgressBar) Set(current int) *ProgressBar {
return pb.Set64(int64(current))
}
// Set64 sets the current value as int64
func (pb *ProgressBar) Set64(current int64) *ProgressBar {
atomic.StoreInt64(&pb.current, current)
return pb
}
// Add to current value
func (pb *ProgressBar) Add(add int) int {
return int(pb.Add64(int64(add)))
}
func (pb *ProgressBar) Add64(add int64) int64 {
return atomic.AddInt64(&pb.current, add)
}
// Set prefix string
func (pb *ProgressBar) Prefix(prefix string) *ProgressBar {
pb.prefix = prefix
return pb
}
// Set postfix string
func (pb *ProgressBar) Postfix(postfix string) *ProgressBar {
pb.postfix = postfix
return pb
}
// Set custom format for bar
// Example: bar.Format("[=>_]")
// Example: bar.Format("[\x00=\x00>\x00-\x00]") // \x00 is the delimiter
func (pb *ProgressBar) Format(format string) *ProgressBar {
var formatEntries []string
if len(format) == 5 {
formatEntries = strings.Split(format, "")
} else {
formatEntries = strings.Split(format, "\x00")
}
if len(formatEntries) == 5 {
pb.BarStart = formatEntries[0]
pb.BarEnd = formatEntries[4]
pb.Empty = formatEntries[3]
pb.Current = formatEntries[1]
pb.CurrentN = formatEntries[2]
}
return pb
}
// Set bar refresh rate
func (pb *ProgressBar) SetRefreshRate(rate time.Duration) *ProgressBar {
pb.RefreshRate = rate
return pb
}
// Set units
// bar.SetUnits(U_NO) - by default
// bar.SetUnits(U_BYTES) - for Mb, Kb, etc
func (pb *ProgressBar) SetUnits(units Units) *ProgressBar {
pb.Units = units
return pb
}
// Set max width, if width is bigger than terminal width, will be ignored
func (pb *ProgressBar) SetMaxWidth(width int) *ProgressBar {
pb.Width = width
pb.ForceWidth = false
return pb
}
// Set bar width
func (pb *ProgressBar) SetWidth(width int) *ProgressBar {
pb.Width = width
pb.ForceWidth = true
return pb
}
// End print
func (pb *ProgressBar) Finish() {
//Protect multiple calls
pb.finishOnce.Do(func() {
close(pb.finish)
pb.write(atomic.LoadInt64(&pb.current))
if !pb.NotPrint {
fmt.Println()
}
pb.isFinish = true
})
}
// End print and write string 'str'
func (pb *ProgressBar) FinishPrint(str string) {
pb.Finish()
fmt.Println(str)
}
// implement io.Writer
func (pb *ProgressBar) Write(p []byte) (n int, err error) {
n = len(p)
pb.Add(n)
return
}
// implement io.Reader
func (pb *ProgressBar) Read(p []byte) (n int, err error) {
n = len(p)
pb.Add(n)
return
}
// Create new proxy reader over bar
func (pb *ProgressBar) NewProxyReader(r io.Reader) *Reader {
return &Reader{r, pb}
}
func (pb *ProgressBar) write(current int64) {
width := pb.GetWidth()
var percentBox, countersBox, timeLeftBox, speedBox, barBox, end, out string
// percents
if pb.ShowPercent {
percent := float64(current) / (float64(pb.Total) / float64(100))
percentBox = fmt.Sprintf(" %.02f %% ", percent)
}
// counters
if pb.ShowCounters {
if pb.Total > 0 {
countersBox = fmt.Sprintf("%s / %s ", Format(current, pb.Units), Format(pb.Total, pb.Units))
} else {
countersBox = Format(current, pb.Units) + " / ? "
}
}
// time left
fromStart := time.Now().Sub(pb.startTime)
currentFromStart := current - pb.startValue
select {
case <-pb.finish:
if pb.ShowFinalTime {
left := (fromStart / time.Second) * time.Second
timeLeftBox = left.String()
}
default:
if pb.ShowTimeLeft && currentFromStart > 0 {
perEntry := fromStart / time.Duration(currentFromStart)
left := time.Duration(pb.Total-currentFromStart) * perEntry
left = (left / time.Second) * time.Second
timeLeftBox = left.String()
}
}
// speed
if pb.ShowSpeed && currentFromStart > 0 {
fromStart := time.Now().Sub(pb.startTime)
speed := float64(currentFromStart) / (float64(fromStart) / float64(time.Second))
speedBox = Format(int64(speed), pb.Units) + "/s "
}
barWidth := escapeAwareRuneCountInString(countersBox + pb.BarStart + pb.BarEnd + percentBox + timeLeftBox + speedBox + pb.prefix + pb.postfix)
// bar
if pb.ShowBar {
size := width - barWidth
if size > 0 {
if pb.Total > 0 {
curCount := int(math.Ceil((float64(current) / float64(pb.Total)) * float64(size)))
emptCount := size - curCount
barBox = pb.BarStart
if emptCount < 0 {
emptCount = 0
}
if curCount > size {
curCount = size
}
if emptCount <= 0 {
barBox += strings.Repeat(pb.Current, curCount)
} else if curCount > 0 {
barBox += strings.Repeat(pb.Current, curCount-1) + pb.CurrentN
}
barBox += strings.Repeat(pb.Empty, emptCount) + pb.BarEnd
} else {
barBox = pb.BarStart
pos := size - int(current)%int(size)
if pos-1 > 0 {
barBox += strings.Repeat(pb.Empty, pos-1)
}
barBox += pb.Current
if size-pos-1 > 0 {
barBox += strings.Repeat(pb.Empty, size-pos-1)
}
barBox += pb.BarEnd
}
}
}
// check len
out = pb.prefix + countersBox + barBox + percentBox + speedBox + timeLeftBox + pb.postfix
if escapeAwareRuneCountInString(out) < width {
end = strings.Repeat(" ", width-utf8.RuneCountInString(out))
}
// and print!
pb.lastPrint = out + end
switch {
case pb.Output != nil:
fmt.Fprint(pb.Output, "\r"+out+end)
case pb.Callback != nil:
pb.Callback(out + end)
case !pb.NotPrint:
fmt.Print("\r" + out + end)
}
}
func (pb *ProgressBar) GetWidth() int {
if pb.ForceWidth {
return pb.Width
}
width := pb.Width
termWidth, _ := terminalWidth()
if width == 0 || termWidth <= width {
width = termWidth
}
return width
}
// Write the current state of the progressbar
func (pb *ProgressBar) Update() {
c := atomic.LoadInt64(&pb.current)
if pb.AlwaysUpdate || c != pb.currentValue {
pb.write(c)
pb.currentValue = c
}
}
func (pb *ProgressBar) String() string {
return pb.lastPrint
}
// Internal loop for writing progressbar
func (pb *ProgressBar) writer() {
pb.Update()
for {
select {
case <-pb.finish:
return
case <-time.After(pb.RefreshRate):
pb.Update()
}
}
}
type window struct {
Row uint16
Col uint16
Xpixel uint16
Ypixel uint16
}

7
vendor/github.com/cheggaaa/pb/pb_nix.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,7 @@
// +build linux darwin freebsd netbsd openbsd dragonfly
package pb
import "syscall"
const sys_ioctl = syscall.SYS_IOCTL

5
vendor/github.com/cheggaaa/pb/pb_solaris.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,5 @@
// +build solaris
package pb
const sys_ioctl = 54

58
vendor/github.com/cheggaaa/pb/pb_test.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,58 @@
package pb
import (
"strings"
"testing"
"github.com/fatih/color"
"github.com/mattn/go-colorable"
)
func Test_IncrementAddsOne(t *testing.T) {
count := 5000
bar := New(count)
expected := 1
actual := bar.Increment()
if actual != expected {
t.Errorf("Expected {%d} was {%d}", expected, actual)
}
}
func Test_Width(t *testing.T) {
count := 5000
bar := New(count)
width := 100
bar.SetWidth(100).Callback = func(out string) {
if len(out) != width {
t.Errorf("Bar width expected {%d} was {%d}", len(out), width)
}
}
bar.Start()
bar.Increment()
bar.Finish()
}
func Test_MultipleFinish(t *testing.T) {
bar := New(5000)
bar.Add(2000)
bar.Finish()
bar.Finish()
}
func Test_Format(t *testing.T) {
bar := New(5000).Format(strings.Join([]string{
color.GreenString("["),
color.New(color.BgGreen).SprintFunc()("o"),
color.New(color.BgHiGreen).SprintFunc()("o"),
color.New(color.BgRed).SprintFunc()("o"),
color.GreenString("]"),
}, "\x00"))
w := colorable.NewColorableStdout()
bar.Callback = func(out string) {
w.Write([]byte(out))
}
bar.Add(2000)
bar.Finish()
bar.Finish()
}

15
vendor/github.com/cheggaaa/pb/pb_win.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,15 @@
// +build windows
package pb
import (
"github.com/olekukonko/ts"
"os"
)
var tty = os.Stdin
func terminalWidth() (int, error) {
size, err := ts.GetSize()
return size.Col(), err
}

108
vendor/github.com/cheggaaa/pb/pb_x.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,108 @@
// +build linux darwin freebsd netbsd openbsd solaris dragonfly
package pb
import (
"errors"
"fmt"
"os"
"os/signal"
"runtime"
"sync"
"syscall"
"unsafe"
)
const (
TIOCGWINSZ = 0x5413
TIOCGWINSZ_OSX = 1074295912
)
var tty *os.File
var ErrPoolWasStarted = errors.New("Bar pool was started")
var echoLocked bool
var echoLockMutex sync.Mutex
func init() {
var err error
tty, err = os.Open("/dev/tty")
if err != nil {
tty = os.Stdin
}
}
func terminalWidth() (int, error) {
w := new(window)
tio := syscall.TIOCGWINSZ
if runtime.GOOS == "darwin" {
tio = TIOCGWINSZ_OSX
}
res, _, err := syscall.Syscall(sys_ioctl,
tty.Fd(),
uintptr(tio),
uintptr(unsafe.Pointer(w)),
)
if int(res) == -1 {
return 0, err
}
return int(w.Col), nil
}
var oldState syscall.Termios
func lockEcho() (quit chan int, err error) {
echoLockMutex.Lock()
defer echoLockMutex.Unlock()
if echoLocked {
err = ErrPoolWasStarted
return
}
echoLocked = true
fd := tty.Fd()
if _, _, e := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0); e != 0 {
err = fmt.Errorf("Can't get terminal settings: %v", e)
return
}
newState := oldState
newState.Lflag &^= syscall.ECHO
newState.Lflag |= syscall.ICANON | syscall.ISIG
newState.Iflag |= syscall.ICRNL
if _, _, e := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); e != 0 {
err = fmt.Errorf("Can't set terminal settings: %v", e)
return
}
quit = make(chan int, 1)
go catchTerminate(quit)
return
}
func unlockEcho() (err error) {
echoLockMutex.Lock()
defer echoLockMutex.Unlock()
if !echoLocked {
return
}
echoLocked = false
fd := tty.Fd()
if _, _, e := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlWriteTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0); e != 0 {
err = fmt.Errorf("Can't set terminal settings")
}
return
}
// listen exit signals and restore terminal state
func catchTerminate(quit chan int) {
sig := make(chan os.Signal, 1)
signal.Notify(sig, os.Interrupt, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGKILL)
defer signal.Stop(sig)
select {
case <-quit:
unlockEcho()
case <-sig:
unlockEcho()
}
}

93
vendor/github.com/cheggaaa/pb/pool.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,93 @@
// +build linux darwin freebsd netbsd openbsd solaris dragonfly
package pb
import (
"fmt"
"sync"
"time"
)
// Create and start new pool with given bars
// You need call pool.Stop() after work
func StartPool(pbs ...*ProgressBar) (pool *Pool, err error) {
pool = new(Pool)
if err = pool.start(); err != nil {
return
}
pool.add(pbs...)
return
}
type Pool struct {
RefreshRate time.Duration
bars []*ProgressBar
quit chan int
finishOnce sync.Once
}
func (p *Pool) add(pbs ...*ProgressBar) {
for _, bar := range pbs {
bar.ManualUpdate = true
bar.NotPrint = true
bar.Start()
p.bars = append(p.bars, bar)
}
}
func (p *Pool) start() (err error) {
p.RefreshRate = DefaultRefreshRate
quit, err := lockEcho()
if err != nil {
return
}
p.quit = make(chan int)
go p.writer(quit)
return
}
func (p *Pool) writer(finish chan int) {
var first = true
for {
select {
case <-time.After(p.RefreshRate):
if p.print(first) {
p.print(false)
finish <- 1
return
}
first = false
case <-p.quit:
finish <- 1
return
}
}
}
func (p *Pool) print(first bool) bool {
var out string
if !first {
out = fmt.Sprintf("\033[%dA", len(p.bars))
}
isFinished := true
for _, bar := range p.bars {
if !bar.isFinish {
isFinished = false
}
bar.Update()
out += fmt.Sprintf("\r%s\n", bar.String())
}
fmt.Print(out)
return isFinished
}
// Restore terminal state and close pool
func (p *Pool) Stop() error {
// Wait until one final refresh has passed.
time.Sleep(p.RefreshRate)
p.finishOnce.Do(func() {
close(p.quit)
})
return unlockEcho()
}

17
vendor/github.com/cheggaaa/pb/reader.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,17 @@
package pb
import (
"io"
)
// It's proxy reader, implement io.Reader
type Reader struct {
io.Reader
bar *ProgressBar
}
func (r *Reader) Read(p []byte) (n int, err error) {
n, err = r.Reader.Read(p)
r.bar.Add(n)
return
}

17
vendor/github.com/cheggaaa/pb/runecount.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,17 @@
package pb
import (
"regexp"
"unicode/utf8"
)
// Finds the control character sequences (like colors)
var ctrlFinder = regexp.MustCompile("\x1b\x5b[0-9]+\x6d")
func escapeAwareRuneCountInString(s string) int {
n := utf8.RuneCountInString(s)
for _, sm := range ctrlFinder.FindAllString(s, -1) {
n -= len(sm)
}
return n
}

16
vendor/github.com/cheggaaa/pb/runecount_test.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,16 @@
package pb
import "testing"
func Test_RuneCount(t *testing.T) {
s := string([]byte{
27, 91, 51, 49, 109, // {Red}
72, 101, 108, 108, 111, // Hello
44, 32, // ,
112, 108, 97, 121, 103, 114, 111, 117, 110, 100, // Playground
27, 91, 48, 109, // {Reset}
})
if e, l := 17, escapeAwareRuneCountInString(s); l != e {
t.Errorf("Invalid length %d, expected %d", l, e)
}
}

8
vendor/github.com/cheggaaa/pb/termios_bsd.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,8 @@
// +build darwin freebsd netbsd openbsd solaris dragonfly
package pb
import "syscall"
const ioctlReadTermios = syscall.TIOCGETA
const ioctlWriteTermios = syscall.TIOCSETA

6
vendor/github.com/cheggaaa/pb/termios_linux.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,6 @@
// +build linux
package pb
const ioctlReadTermios = 0x5401 // syscall.TCGETS
const ioctlWriteTermios = 0x5402 // syscall.TCSETS