From 64b7b6b9759c42b1484e15d5c88f7b50d9ce8e9f Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Fri, 31 May 2024 13:43:57 +0200 Subject: [PATCH] restore/ui: refactor for extensibility --- internal/ui/restore/json.go | 24 ++++++++++++------------ internal/ui/restore/json_test.go | 6 +++--- internal/ui/restore/progress.go | 28 ++++++++++++++++------------ internal/ui/restore/progress_test.go | 24 ++++++++++++------------ internal/ui/restore/text.go | 22 +++++++++++----------- internal/ui/restore/text_test.go | 6 +++--- 6 files changed, 57 insertions(+), 53 deletions(-) diff --git a/internal/ui/restore/json.go b/internal/ui/restore/json.go index c1b95b00b..50d4fe0f7 100644 --- a/internal/ui/restore/json.go +++ b/internal/ui/restore/json.go @@ -20,31 +20,31 @@ func (t *jsonPrinter) print(status interface{}) { t.terminal.Print(ui.ToJSONString(status)) } -func (t *jsonPrinter) Update(filesFinished, filesTotal, allBytesWritten, allBytesTotal uint64, duration time.Duration) { +func (t *jsonPrinter) Update(p State, duration time.Duration) { status := statusUpdate{ MessageType: "status", SecondsElapsed: uint64(duration / time.Second), - TotalFiles: filesTotal, - FilesRestored: filesFinished, - TotalBytes: allBytesTotal, - BytesRestored: allBytesWritten, + TotalFiles: p.FilesTotal, + FilesRestored: p.FilesFinished, + TotalBytes: p.AllBytesTotal, + BytesRestored: p.AllBytesWritten, } - if allBytesTotal > 0 { - status.PercentDone = float64(allBytesWritten) / float64(allBytesTotal) + if p.AllBytesTotal > 0 { + status.PercentDone = float64(p.AllBytesWritten) / float64(p.AllBytesTotal) } t.print(status) } -func (t *jsonPrinter) Finish(filesFinished, filesTotal, allBytesWritten, allBytesTotal uint64, duration time.Duration) { +func (t *jsonPrinter) Finish(p State, duration time.Duration) { status := summaryOutput{ MessageType: "summary", SecondsElapsed: uint64(duration / time.Second), - TotalFiles: filesTotal, - FilesRestored: filesFinished, - TotalBytes: allBytesTotal, - BytesRestored: allBytesWritten, + TotalFiles: p.FilesTotal, + FilesRestored: p.FilesFinished, + TotalBytes: p.AllBytesTotal, + BytesRestored: p.AllBytesWritten, } t.print(status) } diff --git a/internal/ui/restore/json_test.go b/internal/ui/restore/json_test.go index 7bcabb4d7..7ce7b58f3 100644 --- a/internal/ui/restore/json_test.go +++ b/internal/ui/restore/json_test.go @@ -10,20 +10,20 @@ import ( func TestJSONPrintUpdate(t *testing.T) { term := &mockTerm{} printer := NewJSONProgress(term) - printer.Update(3, 11, 29, 47, 5*time.Second) + printer.Update(State{3, 11, 29, 47}, 5*time.Second) test.Equals(t, []string{"{\"message_type\":\"status\",\"seconds_elapsed\":5,\"percent_done\":0.6170212765957447,\"total_files\":11,\"files_restored\":3,\"total_bytes\":47,\"bytes_restored\":29}\n"}, term.output) } func TestJSONPrintSummaryOnSuccess(t *testing.T) { term := &mockTerm{} printer := NewJSONProgress(term) - printer.Finish(11, 11, 47, 47, 5*time.Second) + printer.Finish(State{11, 11, 47, 47}, 5*time.Second) test.Equals(t, []string{"{\"message_type\":\"summary\",\"seconds_elapsed\":5,\"total_files\":11,\"files_restored\":11,\"total_bytes\":47,\"bytes_restored\":47}\n"}, term.output) } func TestJSONPrintSummaryOnErrors(t *testing.T) { term := &mockTerm{} printer := NewJSONProgress(term) - printer.Finish(3, 11, 29, 47, 5*time.Second) + printer.Finish(State{3, 11, 29, 47}, 5*time.Second) test.Equals(t, []string{"{\"message_type\":\"summary\",\"seconds_elapsed\":5,\"total_files\":11,\"files_restored\":3,\"total_bytes\":47,\"bytes_restored\":29}\n"}, term.output) } diff --git a/internal/ui/restore/progress.go b/internal/ui/restore/progress.go index 0e120b6a6..5e501c4b3 100644 --- a/internal/ui/restore/progress.go +++ b/internal/ui/restore/progress.go @@ -7,15 +7,19 @@ import ( "github.com/restic/restic/internal/ui/progress" ) +type State struct { + FilesFinished uint64 + FilesTotal uint64 + AllBytesWritten uint64 + AllBytesTotal uint64 +} + type Progress struct { updater progress.Updater m sync.Mutex progressInfoMap map[string]progressInfoEntry - filesFinished uint64 - filesTotal uint64 - allBytesWritten uint64 - allBytesTotal uint64 + s State started time.Time printer ProgressPrinter @@ -32,8 +36,8 @@ type term interface { } type ProgressPrinter interface { - Update(filesFinished, filesTotal, allBytesWritten, allBytesTotal uint64, duration time.Duration) - Finish(filesFinished, filesTotal, allBytesWritten, allBytesTotal uint64, duration time.Duration) + Update(progress State, duration time.Duration) + Finish(progress State, duration time.Duration) } func NewProgress(printer ProgressPrinter, interval time.Duration) *Progress { @@ -51,9 +55,9 @@ func (p *Progress) update(runtime time.Duration, final bool) { defer p.m.Unlock() if !final { - p.printer.Update(p.filesFinished, p.filesTotal, p.allBytesWritten, p.allBytesTotal, runtime) + p.printer.Update(p.s, runtime) } else { - p.printer.Finish(p.filesFinished, p.filesTotal, p.allBytesWritten, p.allBytesTotal, runtime) + p.printer.Finish(p.s, runtime) } } @@ -66,8 +70,8 @@ func (p *Progress) AddFile(size uint64) { p.m.Lock() defer p.m.Unlock() - p.filesTotal++ - p.allBytesTotal += size + p.s.FilesTotal++ + p.s.AllBytesTotal += size } // AddProgress accumulates the number of bytes written for a file @@ -86,10 +90,10 @@ func (p *Progress) AddProgress(name string, bytesWrittenPortion uint64, bytesTot entry.bytesWritten += bytesWrittenPortion p.progressInfoMap[name] = entry - p.allBytesWritten += bytesWrittenPortion + p.s.AllBytesWritten += bytesWrittenPortion if entry.bytesWritten == entry.bytesTotal { delete(p.progressInfoMap, name) - p.filesFinished++ + p.s.FilesFinished++ } } diff --git a/internal/ui/restore/progress_test.go b/internal/ui/restore/progress_test.go index 9e625aa20..728b74350 100644 --- a/internal/ui/restore/progress_test.go +++ b/internal/ui/restore/progress_test.go @@ -8,7 +8,7 @@ import ( ) type printerTraceEntry struct { - filesFinished, filesTotal, allBytesWritten, allBytesTotal uint64 + progress State duration time.Duration isFinished bool @@ -22,11 +22,11 @@ type mockPrinter struct { const mockFinishDuration = 42 * time.Second -func (p *mockPrinter) Update(filesFinished, filesTotal, allBytesWritten, allBytesTotal uint64, duration time.Duration) { - p.trace = append(p.trace, printerTraceEntry{filesFinished, filesTotal, allBytesWritten, allBytesTotal, duration, false}) +func (p *mockPrinter) Update(progress State, duration time.Duration) { + p.trace = append(p.trace, printerTraceEntry{progress, duration, false}) } -func (p *mockPrinter) Finish(filesFinished, filesTotal, allBytesWritten, allBytesTotal uint64, _ time.Duration) { - p.trace = append(p.trace, printerTraceEntry{filesFinished, filesTotal, allBytesWritten, allBytesTotal, mockFinishDuration, true}) +func (p *mockPrinter) Finish(progress State, _ time.Duration) { + p.trace = append(p.trace, printerTraceEntry{progress, mockFinishDuration, true}) } func testProgress(fn func(progress *Progress) bool) printerTrace { @@ -45,7 +45,7 @@ func TestNew(t *testing.T) { return false }) test.Equals(t, printerTrace{ - printerTraceEntry{0, 0, 0, 0, 0, false}, + printerTraceEntry{State{0, 0, 0, 0}, 0, false}, }, result) } @@ -57,7 +57,7 @@ func TestAddFile(t *testing.T) { return false }) test.Equals(t, printerTrace{ - printerTraceEntry{0, 1, 0, fileSize, 0, false}, + printerTraceEntry{State{0, 1, 0, fileSize}, 0, false}, }, result) } @@ -71,7 +71,7 @@ func TestFirstProgressOnAFile(t *testing.T) { return false }) test.Equals(t, printerTrace{ - printerTraceEntry{0, 1, expectedBytesWritten, expectedBytesTotal, 0, false}, + printerTraceEntry{State{0, 1, expectedBytesWritten, expectedBytesTotal}, 0, false}, }, result) } @@ -86,7 +86,7 @@ func TestLastProgressOnAFile(t *testing.T) { return false }) test.Equals(t, printerTrace{ - printerTraceEntry{1, 1, fileSize, fileSize, 0, false}, + printerTraceEntry{State{1, 1, fileSize, fileSize}, 0, false}, }, result) } @@ -102,7 +102,7 @@ func TestLastProgressOnLastFile(t *testing.T) { return false }) test.Equals(t, printerTrace{ - printerTraceEntry{2, 2, 50 + fileSize, 50 + fileSize, 0, false}, + printerTraceEntry{State{2, 2, 50 + fileSize, 50 + fileSize}, 0, false}, }, result) } @@ -117,7 +117,7 @@ func TestSummaryOnSuccess(t *testing.T) { return true }) test.Equals(t, printerTrace{ - printerTraceEntry{2, 2, 50 + fileSize, 50 + fileSize, mockFinishDuration, true}, + printerTraceEntry{State{2, 2, 50 + fileSize, 50 + fileSize}, mockFinishDuration, true}, }, result) } @@ -132,6 +132,6 @@ func TestSummaryOnErrors(t *testing.T) { return true }) test.Equals(t, printerTrace{ - printerTraceEntry{1, 2, 50 + fileSize/2, 50 + fileSize, mockFinishDuration, true}, + printerTraceEntry{State{1, 2, 50 + fileSize/2, 50 + fileSize}, mockFinishDuration, true}, }, result) } diff --git a/internal/ui/restore/text.go b/internal/ui/restore/text.go index 2647bb28b..9da388e51 100644 --- a/internal/ui/restore/text.go +++ b/internal/ui/restore/text.go @@ -17,30 +17,30 @@ func NewTextProgress(terminal term) ProgressPrinter { } } -func (t *textPrinter) Update(filesFinished, filesTotal, allBytesWritten, allBytesTotal uint64, duration time.Duration) { +func (t *textPrinter) Update(p State, duration time.Duration) { timeLeft := ui.FormatDuration(duration) - formattedAllBytesWritten := ui.FormatBytes(allBytesWritten) - formattedAllBytesTotal := ui.FormatBytes(allBytesTotal) - allPercent := ui.FormatPercent(allBytesWritten, allBytesTotal) + formattedAllBytesWritten := ui.FormatBytes(p.AllBytesWritten) + formattedAllBytesTotal := ui.FormatBytes(p.AllBytesTotal) + allPercent := ui.FormatPercent(p.AllBytesWritten, p.AllBytesTotal) progress := fmt.Sprintf("[%s] %s %v files/dirs %s, total %v files/dirs %v", - timeLeft, allPercent, filesFinished, formattedAllBytesWritten, filesTotal, formattedAllBytesTotal) + timeLeft, allPercent, p.FilesFinished, formattedAllBytesWritten, p.FilesTotal, formattedAllBytesTotal) t.terminal.SetStatus([]string{progress}) } -func (t *textPrinter) Finish(filesFinished, filesTotal, allBytesWritten, allBytesTotal uint64, duration time.Duration) { +func (t *textPrinter) Finish(p State, duration time.Duration) { t.terminal.SetStatus([]string{}) timeLeft := ui.FormatDuration(duration) - formattedAllBytesTotal := ui.FormatBytes(allBytesTotal) + formattedAllBytesTotal := ui.FormatBytes(p.AllBytesTotal) var summary string - if filesFinished == filesTotal && allBytesWritten == allBytesTotal { - summary = fmt.Sprintf("Summary: Restored %d files/dirs (%s) in %s", filesTotal, formattedAllBytesTotal, timeLeft) + if p.FilesFinished == p.FilesTotal && p.AllBytesWritten == p.AllBytesTotal { + summary = fmt.Sprintf("Summary: Restored %d files/dirs (%s) in %s", p.FilesTotal, formattedAllBytesTotal, timeLeft) } else { - formattedAllBytesWritten := ui.FormatBytes(allBytesWritten) + formattedAllBytesWritten := ui.FormatBytes(p.AllBytesWritten) summary = fmt.Sprintf("Summary: Restored %d / %d files/dirs (%s / %s) in %s", - filesFinished, filesTotal, formattedAllBytesWritten, formattedAllBytesTotal, timeLeft) + p.FilesFinished, p.FilesTotal, formattedAllBytesWritten, formattedAllBytesTotal, timeLeft) } t.terminal.Print(summary) diff --git a/internal/ui/restore/text_test.go b/internal/ui/restore/text_test.go index fc03904ff..2a1723943 100644 --- a/internal/ui/restore/text_test.go +++ b/internal/ui/restore/text_test.go @@ -22,20 +22,20 @@ func (m *mockTerm) SetStatus(lines []string) { func TestPrintUpdate(t *testing.T) { term := &mockTerm{} printer := NewTextProgress(term) - printer.Update(3, 11, 29, 47, 5*time.Second) + printer.Update(State{3, 11, 29, 47}, 5*time.Second) test.Equals(t, []string{"[0:05] 61.70% 3 files/dirs 29 B, total 11 files/dirs 47 B"}, term.output) } func TestPrintSummaryOnSuccess(t *testing.T) { term := &mockTerm{} printer := NewTextProgress(term) - printer.Finish(11, 11, 47, 47, 5*time.Second) + printer.Finish(State{11, 11, 47, 47}, 5*time.Second) test.Equals(t, []string{"Summary: Restored 11 files/dirs (47 B) in 0:05"}, term.output) } func TestPrintSummaryOnErrors(t *testing.T) { term := &mockTerm{} printer := NewTextProgress(term) - printer.Finish(3, 11, 29, 47, 5*time.Second) + printer.Finish(State{3, 11, 29, 47}, 5*time.Second) test.Equals(t, []string{"Summary: Restored 3 / 11 files/dirs (29 B / 47 B) in 0:05"}, term.output) }