restic: introduce Counter interface to decouple from ui/progress (#21861)

decouple restic and ui/progress packages
This commit is contained in:
Michael Eischer
2026-06-13 21:08:18 +02:00
committed by GitHub
parent 22a304af7e
commit 8e11f5747d
37 changed files with 181 additions and 145 deletions
+7 -9
View File
@@ -3,6 +3,8 @@ package progress
import (
"sync/atomic"
"time"
"github.com/restic/restic/internal/restic"
)
// A Func is a callback for a Counter.
@@ -20,6 +22,8 @@ type Counter struct {
value, max atomic.Uint64
}
var _ restic.Counter = (*Counter)(nil)
// NewCounter starts a new Counter.
func NewCounter(interval time.Duration, total uint64, report Func) *Counter {
c := new(Counter)
@@ -35,16 +39,12 @@ func NewCounter(interval time.Duration, total uint64, report Func) *Counter {
// Add v to the Counter. This method is concurrency-safe.
func (c *Counter) Add(v uint64) {
if c != nil {
c.value.Add(v)
}
c.value.Add(v)
}
// SetMax sets the maximum expected counter value. This method is concurrency-safe.
func (c *Counter) SetMax(max uint64) {
if c != nil {
c.max.Store(max)
}
c.max.Store(max)
}
// Get returns the current value and the maximum of c.
@@ -54,7 +54,5 @@ func (c *Counter) Get() (v, max uint64) {
}
func (c *Counter) Done() {
if c != nil {
c.Updater.Done()
}
c.Updater.Done()
}
-8
View File
@@ -58,11 +58,3 @@ func TestCounter(t *testing.T) {
t.Log("number of calls:", ncalls)
}
func TestCounterNil(_ *testing.T) {
// Shouldn't panic.
var c *progress.Counter
c.Add(1)
c.SetMax(42)
c.Done()
}
+10 -6
View File
@@ -1,14 +1,18 @@
package progress
import (
"github.com/restic/restic/internal/restic"
)
// A Printer can can return a new counter or print messages
// at different log levels.
// It must be safe to call its methods from concurrent goroutines.
type Printer interface {
// NewCounter returns a new progress counter. It is not shown if --quiet or --json is specified.
NewCounter(description string) *Counter
NewCounter(description string) restic.Counter
// NewCounterTerminalOnly returns a new progress counter that is only shown if stdout points to a
// terminal. It is not shown if --quiet or --json is specified.
NewCounterTerminalOnly(description string) *Counter
NewCounterTerminalOnly(description string) restic.Counter
// E reports an error. This message is always printed to stderr.
// Appends a newline if not present.
@@ -42,12 +46,12 @@ func NewNoopPrinter() Printer {
return &noopPrinter{}
}
func (*noopPrinter) NewCounter(_ string) *Counter {
return nil
func (*noopPrinter) NewCounter(_ string) restic.Counter {
return restic.NoopCounter
}
func (*noopPrinter) NewCounterTerminalOnly(_ string) *Counter {
return nil
func (*noopPrinter) NewCounterTerminalOnly(_ string) restic.Counter {
return restic.NoopCounter
}
func (*noopPrinter) E(_ string, _ ...interface{}) {}
+5 -4
View File
@@ -6,6 +6,7 @@ import (
"strconv"
"time"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui"
)
@@ -27,9 +28,9 @@ func CalculateProgressInterval(show bool, json bool, canUpdateStatus bool) time.
}
// newProgressMax returns a progress.Counter that prints to terminal if provided.
func newProgressMax(show bool, max uint64, description string, term ui.Terminal) *Counter {
func newProgressMax(show bool, max uint64, description string, term ui.Terminal) restic.Counter {
if !show {
return nil
return restic.NoopCounter
}
interval := CalculateProgressInterval(show, false, term.CanUpdateStatus())
@@ -57,11 +58,11 @@ type terminalPrinter struct {
v uint
}
func (t *terminalPrinter) NewCounter(description string) *Counter {
func (t *terminalPrinter) NewCounter(description string) restic.Counter {
return newProgressMax(t.v > 0, 0, description, t.term)
}
func (t *terminalPrinter) NewCounterTerminalOnly(description string) *Counter {
func (t *terminalPrinter) NewCounterTerminalOnly(description string) restic.Counter {
return newProgressMax(t.v > 0 && t.term.OutputIsTerminal(), 0, description, t.term)
}