crunchy-cli/cli/commands/utils.go
2022-07-26 12:47:23 +02:00

125 lines
2.6 KiB
Go

package commands
import (
"fmt"
"github.com/crunchy-labs/crunchy-cli/utils"
"os"
"os/exec"
"runtime"
"strconv"
"strings"
"sync"
)
type DownloadProgress struct {
Prefix string
Message string
Total int
Current int
Dev bool
Quiet bool
lock sync.Mutex
}
func (dp *DownloadProgress) Update() {
dp.update("", false)
}
func (dp *DownloadProgress) UpdateMessage(msg string, permanent bool) {
dp.update(msg, permanent)
}
func (dp *DownloadProgress) update(msg string, permanent bool) {
if dp.Quiet {
return
}
if dp.Current >= dp.Total {
return
}
dp.lock.Lock()
defer dp.lock.Unlock()
dp.Current++
if msg == "" {
msg = dp.Message
}
if permanent {
dp.Message = msg
}
if dp.Dev {
fmt.Printf("%s%s\n", dp.Prefix, msg)
return
}
percentage := float32(dp.Current) / float32(dp.Total) * 100
pre := fmt.Sprintf("%s%s [", dp.Prefix, msg)
post := fmt.Sprintf("]%4d%% %8d/%d", int(percentage), dp.Current, dp.Total)
// I don't really know why +2 is needed here but without it the Printf below would not print to the line end
progressWidth := terminalWidth() - len(pre) - len(post) + 2
repeatCount := int(percentage / float32(100) * float32(progressWidth))
// it can be lower than zero when the terminal is very tiny
if repeatCount < 0 {
repeatCount = 0
}
progressPercentage := strings.Repeat("=", repeatCount)
if dp.Current != dp.Total {
progressPercentage += ">"
}
fmt.Printf("\r%s%-"+fmt.Sprint(progressWidth)+"s%s", pre, progressPercentage, post)
}
func terminalWidth() int {
if runtime.GOOS != "windows" {
cmd := exec.Command("stty", "size")
cmd.Stdin = os.Stdin
res, err := cmd.Output()
if err != nil {
return 60
}
// on alpine linux the command `stty size` does not respond the terminal size
// but something like "stty: standard input". this may also apply to other systems
splitOutput := strings.SplitN(strings.ReplaceAll(string(res), "\n", ""), " ", 2)
if len(splitOutput) == 1 {
return 60
}
width, err := strconv.Atoi(splitOutput[1])
if err != nil {
return 60
}
return width
}
return 60
}
func LoadCrunchy() error {
var encryptionKey []byte
if utils.IsTempSession() {
encryptionKey = nil
} else {
if encrypted, err := utils.IsSavedSessionEncrypted(); err != nil {
if os.IsNotExist(err) {
return fmt.Errorf("to use this command, login first. Type `%s login -h` to get help", os.Args[0])
}
return err
} else if encrypted {
encryptionKey, err = ReadLineSilent()
if err != nil {
return fmt.Errorf("failed to read password")
}
}
}
var err error
utils.Crunchy, err = utils.LoadSession(encryptionKey)
return err
}