peridot/utils/progress.go
2022-07-07 22:13:21 +02:00

156 lines
4.6 KiB
Go

// Copyright (c) All respective contributors to the Peridot Project. All rights reserved.
// Copyright (c) 2021-2022 Rocky Enterprise Software Foundation, Inc. All rights reserved.
// Copyright (c) 2021-2022 Ctrl IQ, Inc. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. 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.
//
// 3. Neither the name of the copyright holder 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.
package utils
import (
"fmt"
"github.com/fatih/color"
"github.com/vbauerster/mpb/v7"
"github.com/vbauerster/mpb/v7/decor"
"strings"
"unicode/utf8"
)
type Progress struct {
Instance *mpb.Progress
Bar *mpb.Bar
StatusBars []*mpb.Bar
KeepLogs bool
MaxLinesNum int
MaxLinesNumAfterFinish int
statusHeader string
progressText string
successText string
failedText string
statusLines []string
isFinished bool
didFail bool
}
func NewProgress(instance *mpb.Progress, progressText string, successText string, failedText string) *Progress {
if instance == nil {
return &Progress{}
}
p := &Progress{
Instance: instance,
MaxLinesNum: 15,
MaxLinesNumAfterFinish: 10,
progressText: progressText,
successText: successText,
failedText: failedText,
}
p.setStatusHeader(p.progressText)
p.Bar = p.Instance.Add(1,
mpb.NewBarFiller(mpb.BarStyle().Tip(`-`, `\`, `|`, `/`).Filler("").Padding(" ").Lbound("").Rbound("")),
mpb.PrependDecorators(decor.Any(func(_ decor.Statistics) string {
if p.isFinished {
if p.didFail {
return color.RedString(p.statusHeader)
}
return color.GreenString(p.statusHeader)
} else {
return color.CyanString(p.statusHeader)
}
})),
)
for i := 0; i < p.MaxLinesNum; i++ {
p.statusLines = append(p.statusLines, "...")
}
for i := 0; i < p.MaxLinesNum; i++ {
currentNum := i
statusBar := p.Instance.Add(1, mpb.NewBarFiller(mpb.BarStyle().Lbound("").Rbound("").Padding(" ").Filler(" ")), mpb.PrependDecorators(
decor.Any(func(s decor.Statistics) string {
return fmt.Sprintf(" => %s", p.statusLines[currentNum])
}),
))
p.StatusBars = append(p.StatusBars, statusBar)
}
return p
}
func (p *Progress) AppendStatus(format string, args ...interface{}) {
if p.Instance == nil {
return
}
statusLine := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(strings.TrimSpace(fmt.Sprintf(format, args...)), "\n", ""), "\r\n", ""), "\r", ""), "\x00", "")
if !utf8.ValidString(statusLine) {
v := make([]rune, 0, len(statusLine))
for i, r := range statusLine {
if r == utf8.RuneError {
_, size := utf8.DecodeRuneInString(statusLine[i:])
if size == 1 {
continue
}
}
v = append(v, r)
}
statusLine = string(v)
}
p.statusLines = append(p.statusLines[1:], statusLine)
}
func (p *Progress) Finish(didFail bool) {
if p.Instance == nil {
return
}
p.didFail = didFail
if didFail {
p.setStatusHeader(p.failedText)
} else {
p.setStatusHeader(p.successText)
}
for i, bar := range p.StatusBars {
if len(p.StatusBars) > p.MaxLinesNumAfterFinish && i < p.MaxLinesNum-p.MaxLinesNumAfterFinish-1 {
bar.Abort(true)
} else {
bar.Abort(!p.KeepLogs)
}
}
p.Bar.Increment()
p.isFinished = true
}
func (p *Progress) setStatusHeader(header string) {
p.statusHeader = fmt.Sprintf(" ==> %s", header)
}