2020-08-02 10:22:06 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2020-11-04 13:11:29 +00:00
|
|
|
"os"
|
|
|
|
"strconv"
|
2021-08-29 12:55:33 +00:00
|
|
|
"strings"
|
2020-08-02 10:22:06 +00:00
|
|
|
"time"
|
|
|
|
|
2022-10-21 15:34:14 +00:00
|
|
|
"github.com/restic/restic/internal/ui"
|
2020-11-04 13:11:29 +00:00
|
|
|
"github.com/restic/restic/internal/ui/progress"
|
2021-08-29 12:55:33 +00:00
|
|
|
"github.com/restic/restic/internal/ui/termstatus"
|
2020-08-02 10:22:06 +00:00
|
|
|
)
|
|
|
|
|
2020-12-29 13:56:14 +00:00
|
|
|
// calculateProgressInterval returns the interval configured via RESTIC_PROGRESS_FPS
|
|
|
|
// or if unset returns an interval for 60fps on interactive terminals and 0 (=disabled)
|
2021-02-15 20:59:33 +00:00
|
|
|
// for non-interactive terminals or when run using the --quiet flag
|
2021-08-18 11:03:25 +00:00
|
|
|
func calculateProgressInterval(show bool, json bool) time.Duration {
|
2020-11-04 13:11:29 +00:00
|
|
|
interval := time.Second / 60
|
2020-12-29 14:24:02 +00:00
|
|
|
fps, err := strconv.ParseFloat(os.Getenv("RESTIC_PROGRESS_FPS"), 64)
|
|
|
|
if err == nil && fps > 0 {
|
2020-12-28 22:15:14 +00:00
|
|
|
if fps > 60 {
|
|
|
|
fps = 60
|
2020-11-04 13:11:29 +00:00
|
|
|
}
|
2020-12-29 14:24:02 +00:00
|
|
|
interval = time.Duration(float64(time.Second) / fps)
|
2021-08-18 11:03:25 +00:00
|
|
|
} else if !json && !stdoutCanUpdateStatus() || !show {
|
2020-12-28 22:15:14 +00:00
|
|
|
interval = 0
|
2020-11-04 13:11:29 +00:00
|
|
|
}
|
2020-12-29 13:56:14 +00:00
|
|
|
return interval
|
|
|
|
}
|
|
|
|
|
2023-07-16 02:48:30 +00:00
|
|
|
// newTerminalProgressMax returns a progress.Counter that prints to stdout or terminal if provided.
|
2024-01-20 17:34:11 +00:00
|
|
|
func newGenericProgressMax(show bool, max uint64, description string, print func(status string, final bool)) *progress.Counter {
|
2020-12-29 13:56:14 +00:00
|
|
|
if !show {
|
|
|
|
return nil
|
|
|
|
}
|
2021-08-18 11:03:25 +00:00
|
|
|
interval := calculateProgressInterval(show, false)
|
2020-08-02 10:22:06 +00:00
|
|
|
|
2022-12-29 11:29:46 +00:00
|
|
|
return progress.NewCounter(interval, max, func(v uint64, max uint64, d time.Duration, final bool) {
|
2020-12-05 22:57:06 +00:00
|
|
|
var status string
|
|
|
|
if max == 0 {
|
2022-10-21 15:34:14 +00:00
|
|
|
status = fmt.Sprintf("[%s] %d %s",
|
|
|
|
ui.FormatDuration(d), v, description)
|
2020-12-05 22:57:06 +00:00
|
|
|
} else {
|
|
|
|
status = fmt.Sprintf("[%s] %s %d / %d %s",
|
2022-10-21 15:34:14 +00:00
|
|
|
ui.FormatDuration(d), ui.FormatPercent(v, max), v, max, description)
|
2020-12-05 22:57:06 +00:00
|
|
|
}
|
2020-08-02 10:22:06 +00:00
|
|
|
|
2024-01-20 17:34:11 +00:00
|
|
|
print(status, final)
|
2020-11-04 13:11:29 +00:00
|
|
|
})
|
2020-08-02 10:22:06 +00:00
|
|
|
}
|
2021-08-29 12:55:33 +00:00
|
|
|
|
2023-07-16 02:48:30 +00:00
|
|
|
func newTerminalProgressMax(show bool, max uint64, description string, term *termstatus.Terminal) *progress.Counter {
|
2024-01-20 17:34:11 +00:00
|
|
|
return newGenericProgressMax(show, max, description, func(status string, final bool) {
|
|
|
|
if final {
|
|
|
|
term.SetStatus([]string{})
|
|
|
|
term.Print(status)
|
|
|
|
} else {
|
|
|
|
term.SetStatus([]string{status})
|
|
|
|
}
|
2023-07-16 02:48:30 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// newProgressMax calls newTerminalProgress without a terminal (print to stdout)
|
|
|
|
func newProgressMax(show bool, max uint64, description string) *progress.Counter {
|
|
|
|
return newGenericProgressMax(show, max, description, printProgress)
|
|
|
|
}
|
|
|
|
|
2024-01-20 17:34:11 +00:00
|
|
|
func printProgress(status string, final bool) {
|
2023-07-16 02:48:30 +00:00
|
|
|
|
|
|
|
canUpdateStatus := stdoutCanUpdateStatus()
|
|
|
|
|
2021-08-29 12:55:33 +00:00
|
|
|
w := stdoutTerminalWidth()
|
|
|
|
if w > 0 {
|
|
|
|
if w < 3 {
|
|
|
|
status = termstatus.Truncate(status, w)
|
|
|
|
} else {
|
2022-05-31 16:41:46 +00:00
|
|
|
trunc := termstatus.Truncate(status, w-3)
|
|
|
|
if len(trunc) < len(status) {
|
|
|
|
status = trunc + "..."
|
|
|
|
}
|
2021-08-29 12:55:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var carriageControl, clear string
|
|
|
|
|
|
|
|
if canUpdateStatus {
|
|
|
|
clear = clearLine(w)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !(strings.HasSuffix(status, "\r") || strings.HasSuffix(status, "\n")) {
|
|
|
|
if canUpdateStatus {
|
|
|
|
carriageControl = "\r"
|
|
|
|
} else {
|
|
|
|
carriageControl = "\n"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_, _ = os.Stdout.Write([]byte(clear + status + carriageControl))
|
2024-01-20 17:34:11 +00:00
|
|
|
if final {
|
|
|
|
_, _ = os.Stdout.Write([]byte("\n"))
|
|
|
|
}
|
2021-08-29 12:55:33 +00:00
|
|
|
}
|
2023-10-01 17:38:09 +00:00
|
|
|
|
|
|
|
func newIndexProgress(quiet bool, json bool) *progress.Counter {
|
2023-10-23 19:16:52 +00:00
|
|
|
return newProgressMax(!quiet && !json && stdoutIsTerminal(), 0, "index files loaded")
|
2023-10-01 17:38:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func newIndexTerminalProgress(quiet bool, json bool, term *termstatus.Terminal) *progress.Counter {
|
2023-10-23 19:16:52 +00:00
|
|
|
return newTerminalProgressMax(!quiet && !json && stdoutIsTerminal(), 0, "index files loaded", term)
|
2023-10-01 17:38:09 +00:00
|
|
|
}
|
2024-01-20 17:40:22 +00:00
|
|
|
|
|
|
|
type terminalProgressPrinter struct {
|
|
|
|
term *termstatus.Terminal
|
|
|
|
ui.Message
|
|
|
|
show bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *terminalProgressPrinter) NewCounter(description string) *progress.Counter {
|
|
|
|
return newTerminalProgressMax(t.show, 0, description, t.term)
|
|
|
|
}
|
|
|
|
|
|
|
|
func newTerminalProgressPrinter(verbosity uint, term *termstatus.Terminal) progress.Printer {
|
|
|
|
return &terminalProgressPrinter{
|
|
|
|
term: term,
|
|
|
|
Message: *ui.NewMessage(term, verbosity),
|
|
|
|
show: verbosity > 0,
|
|
|
|
}
|
|
|
|
}
|