Move progress into pkg
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
82
pkg/progress/bar.go
Normal file
82
pkg/progress/bar.go
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package progress
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// TODO(stevvooe): We may want to support more interesting parameterization of
|
||||
// the bar. For now, it is very simple.
|
||||
|
||||
// Bar provides a very simple progress bar implementation.
|
||||
//
|
||||
// Use with fmt.Printf and "r" to format the progress bar. A "-" flag makes it
|
||||
// progress from right to left.
|
||||
type Bar float64
|
||||
|
||||
var _ fmt.Formatter = Bar(1.0)
|
||||
|
||||
// Format the progress bar as output
|
||||
func (h Bar) Format(state fmt.State, r rune) {
|
||||
switch r {
|
||||
case 'r':
|
||||
default:
|
||||
panic(fmt.Sprintf("%v: unexpected format character", float64(h)))
|
||||
}
|
||||
|
||||
if h > 1.0 {
|
||||
h = 1.0
|
||||
}
|
||||
|
||||
if h < 0.0 {
|
||||
h = 0.0
|
||||
}
|
||||
|
||||
if state.Flag('-') {
|
||||
h = 1.0 - h
|
||||
}
|
||||
|
||||
width, ok := state.Width()
|
||||
if !ok {
|
||||
// default width of 40
|
||||
width = 40
|
||||
}
|
||||
|
||||
var pad int
|
||||
|
||||
extra := len([]byte(green)) + len([]byte(reset))
|
||||
|
||||
p := make([]byte, width+extra)
|
||||
p[0], p[len(p)-1] = '|', '|'
|
||||
pad += 2
|
||||
|
||||
positive := int(Bar(width-pad) * h)
|
||||
negative := width - pad - positive
|
||||
|
||||
n := 1
|
||||
n += copy(p[n:], []byte(green))
|
||||
n += copy(p[n:], bytes.Repeat([]byte("+"), positive))
|
||||
n += copy(p[n:], []byte(reset))
|
||||
|
||||
if negative > 0 {
|
||||
copy(p[n:len(p)-1], bytes.Repeat([]byte("-"), negative))
|
||||
}
|
||||
|
||||
state.Write(p)
|
||||
}
|
||||
18
pkg/progress/doc.go
Normal file
18
pkg/progress/doc.go
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package progress assists in displaying human readable progress information.
|
||||
package progress
|
||||
24
pkg/progress/escape.go
Normal file
24
pkg/progress/escape.go
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package progress
|
||||
|
||||
const (
|
||||
escape = "\x1b"
|
||||
reset = escape + "[0m"
|
||||
red = escape + "[31m" // nolint: unused, varcheck
|
||||
green = escape + "[32m"
|
||||
)
|
||||
45
pkg/progress/humaans.go
Normal file
45
pkg/progress/humaans.go
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package progress
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
units "github.com/docker/go-units"
|
||||
)
|
||||
|
||||
// Bytes converts a regular int64 to human readable type.
|
||||
type Bytes int64
|
||||
|
||||
// String returns the string representation of bytes
|
||||
func (b Bytes) String() string {
|
||||
return units.CustomSize("%02.1f %s", float64(b), 1024.0, []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"})
|
||||
}
|
||||
|
||||
// BytesPerSecond is the rate in seconds for byte operations
|
||||
type BytesPerSecond int64
|
||||
|
||||
// NewBytesPerSecond returns the rate that n bytes were written in the provided duration
|
||||
func NewBytesPerSecond(n int64, duration time.Duration) BytesPerSecond {
|
||||
return BytesPerSecond(float64(n) / duration.Seconds())
|
||||
}
|
||||
|
||||
// String returns the string representation of the rate
|
||||
func (bps BytesPerSecond) String() string {
|
||||
return fmt.Sprintf("%v/s", Bytes(bps))
|
||||
}
|
||||
115
pkg/progress/writer.go
Normal file
115
pkg/progress/writer.go
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package progress
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/console"
|
||||
)
|
||||
|
||||
var (
|
||||
regexCleanLine = regexp.MustCompile("\x1b\\[[0-9]+m[\x1b]?")
|
||||
)
|
||||
|
||||
// Writer buffers writes until flush, at which time the last screen is cleared
|
||||
// and the current buffer contents are written. This is useful for
|
||||
// implementing progress displays, such as those implemented in docker and
|
||||
// git.
|
||||
type Writer struct {
|
||||
buf bytes.Buffer
|
||||
w io.Writer
|
||||
lines int
|
||||
}
|
||||
|
||||
// NewWriter returns a writer
|
||||
func NewWriter(w io.Writer) *Writer {
|
||||
return &Writer{
|
||||
w: w,
|
||||
}
|
||||
}
|
||||
|
||||
// Write the provided bytes
|
||||
func (w *Writer) Write(p []byte) (n int, err error) {
|
||||
return w.buf.Write(p)
|
||||
}
|
||||
|
||||
// Flush should be called when refreshing the current display.
|
||||
func (w *Writer) Flush() error {
|
||||
if w.buf.Len() == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := w.clearLines(); err != nil {
|
||||
return err
|
||||
}
|
||||
w.lines = countLines(w.buf.String())
|
||||
|
||||
if _, err := w.w.Write(w.buf.Bytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.buf.Reset()
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO(stevvooe): The following are system specific. Break these out if we
|
||||
// decide to build this package further.
|
||||
|
||||
func (w *Writer) clearLines() error {
|
||||
for i := 0; i < w.lines; i++ {
|
||||
if _, err := fmt.Fprintf(w.w, "\x1b[1A\x1b[2K\r"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// countLines in the output. If a line is longer than the console width then
|
||||
// an extra line is added to the count for each wrapped line. If the console
|
||||
// width is undefined then 0 is returned so that no lines are cleared on the next
|
||||
// flush.
|
||||
func countLines(output string) int {
|
||||
con, err := console.ConsoleFromFile(os.Stdin)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
ws, err := con.Size()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
width := int(ws.Width)
|
||||
if width <= 0 {
|
||||
return 0
|
||||
}
|
||||
strlines := strings.Split(output, "\n")
|
||||
lines := -1
|
||||
for _, line := range strlines {
|
||||
lines += (len(stripLine(line))-1)/width + 1
|
||||
}
|
||||
return lines
|
||||
}
|
||||
|
||||
func stripLine(line string) string {
|
||||
return string(regexCleanLine.ReplaceAll([]byte(line), []byte{}))
|
||||
}
|
||||
Reference in New Issue
Block a user