tui/progress/progress.go

184 lines
3.1 KiB
Go
Raw Permalink Normal View History

2024-07-04 21:22:54 +02:00
package progress
import (
"fmt"
"math"
"regexp"
"strconv"
"strings"
"sync"
"golang.org/x/term"
)
var regex = regexp.MustCompile("\033\\[[0-9;]+m")
//║░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░║ 40 characters
type ProgressInfo struct {
Size int64
Desc string
ClearOnFinish bool
}
type ProgressBar struct {
lock sync.Mutex
maxBytes int64
currentBytes int64
hasStarted bool
currentPercent int
desc string
clearFinish bool
textWidth int
}
func New(info ProgressInfo) *ProgressBar {
return &ProgressBar{
maxBytes: info.Size,
desc: info.Desc,
clearFinish: info.ClearOnFinish,
}
}
func (p *ProgressBar) render(final bool) {
p.lock.Lock()
defer p.lock.Unlock()
var sb strings.Builder
numFilled := math.Round((float64(p.currentBytes) / float64(p.maxBytes)) * 40)
numBlank := 40 - numFilled
donePercent := math.Round(float64(p.currentBytes) / float64(p.maxBytes) * 100)
if int(donePercent) == p.currentPercent {
return
}
var blockColor string
if final {
blockColor = "34"
} else {
blockColor = "214"
}
sb.WriteString(p.desc)
sb.WriteString(" ")
percentString := strconv.Itoa(int(donePercent)) + "%"
blankPercent := 4 - len(percentString)
for i := 0; i < blankPercent; i++ {
sb.WriteString(" ")
}
sb.WriteString(percentString)
sb.WriteString("\033[1m\033[38;5;247m [\033[0m")
for i := 0; i < int(numFilled); i++ {
sb.WriteString(fmt.Sprintf("\033[38;5;%sm█\033[0m", blockColor))
}
if numFilled < 40 {
numBlank = numBlank - 1
sb.WriteString("\033[38;5;214m▒\033[0m")
for i := 0; i < int(numBlank); i++ {
sb.WriteString("\033[38;5;247m░\033[0m")
}
} else {
for i := 0; i < int(numBlank); i++ {
sb.WriteString("\033[38;5;247m░\033[0m")
}
}
sb.WriteString("\033[1m\033[38;5;247m]\033[0m")
width, _, err := term.GetSize(0)
if err != nil {
return
}
var lineNum int
if width == 0 {
lineNum = 1
} else {
lineNum = ((len(removeANSIEscapeCodes(sb.String())) + width - 1) / width)
}
if p.hasStarted {
for i := 0; i < lineNum; i++ {
if i == lineNum-1 {
fmt.Println("\033[A\033[0G\033[K" + sb.String())
} else {
fmt.Println("\033[A\033[0G\033[K")
}
}
} else {
fmt.Println(sb.String())
p.hasStarted = true
}
p.textWidth = len(removeANSIEscapeCodes(sb.String()))
}
func (p *ProgressBar) Add(num int64) {
p.currentBytes = p.currentBytes + num
p.render(false)
}
func (p *ProgressBar) Close() (err error) {
if p.clearFinish {
width, _, err := term.GetSize(0)
if err != nil {
return err
}
var lineNum int
if width == 0 {
lineNum = 1
} else {
lineNum = ((p.textWidth + width - 1) / width)
}
for i := 0; i < lineNum; i++ {
fmt.Println("\033[A\033[0G\033[K")
}
} else {
p.render(true)
}
return
}
func (p *ProgressBar) Write(b []byte) (n int, err error) {
n = len(b)
p.Add(int64(n))
return
}
func (p *ProgressBar) Read(b []byte) (n int, err error) {
p.Add(int64(n))
return
}
func removeANSIEscapeCodes(input string) string {
return regex.ReplaceAllString(input, "")
}