forked from TrueCloudLab/restic
restore/ui: refactor for extensibility
This commit is contained in:
parent
6a4ae9d6b1
commit
64b7b6b975
6 changed files with 57 additions and 53 deletions
|
@ -20,31 +20,31 @@ func (t *jsonPrinter) print(status interface{}) {
|
||||||
t.terminal.Print(ui.ToJSONString(status))
|
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{
|
status := statusUpdate{
|
||||||
MessageType: "status",
|
MessageType: "status",
|
||||||
SecondsElapsed: uint64(duration / time.Second),
|
SecondsElapsed: uint64(duration / time.Second),
|
||||||
TotalFiles: filesTotal,
|
TotalFiles: p.FilesTotal,
|
||||||
FilesRestored: filesFinished,
|
FilesRestored: p.FilesFinished,
|
||||||
TotalBytes: allBytesTotal,
|
TotalBytes: p.AllBytesTotal,
|
||||||
BytesRestored: allBytesWritten,
|
BytesRestored: p.AllBytesWritten,
|
||||||
}
|
}
|
||||||
|
|
||||||
if allBytesTotal > 0 {
|
if p.AllBytesTotal > 0 {
|
||||||
status.PercentDone = float64(allBytesWritten) / float64(allBytesTotal)
|
status.PercentDone = float64(p.AllBytesWritten) / float64(p.AllBytesTotal)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.print(status)
|
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{
|
status := summaryOutput{
|
||||||
MessageType: "summary",
|
MessageType: "summary",
|
||||||
SecondsElapsed: uint64(duration / time.Second),
|
SecondsElapsed: uint64(duration / time.Second),
|
||||||
TotalFiles: filesTotal,
|
TotalFiles: p.FilesTotal,
|
||||||
FilesRestored: filesFinished,
|
FilesRestored: p.FilesFinished,
|
||||||
TotalBytes: allBytesTotal,
|
TotalBytes: p.AllBytesTotal,
|
||||||
BytesRestored: allBytesWritten,
|
BytesRestored: p.AllBytesWritten,
|
||||||
}
|
}
|
||||||
t.print(status)
|
t.print(status)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,20 +10,20 @@ import (
|
||||||
func TestJSONPrintUpdate(t *testing.T) {
|
func TestJSONPrintUpdate(t *testing.T) {
|
||||||
term := &mockTerm{}
|
term := &mockTerm{}
|
||||||
printer := NewJSONProgress(term)
|
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)
|
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) {
|
func TestJSONPrintSummaryOnSuccess(t *testing.T) {
|
||||||
term := &mockTerm{}
|
term := &mockTerm{}
|
||||||
printer := NewJSONProgress(term)
|
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)
|
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) {
|
func TestJSONPrintSummaryOnErrors(t *testing.T) {
|
||||||
term := &mockTerm{}
|
term := &mockTerm{}
|
||||||
printer := NewJSONProgress(term)
|
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)
|
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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,15 +7,19 @@ import (
|
||||||
"github.com/restic/restic/internal/ui/progress"
|
"github.com/restic/restic/internal/ui/progress"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type State struct {
|
||||||
|
FilesFinished uint64
|
||||||
|
FilesTotal uint64
|
||||||
|
AllBytesWritten uint64
|
||||||
|
AllBytesTotal uint64
|
||||||
|
}
|
||||||
|
|
||||||
type Progress struct {
|
type Progress struct {
|
||||||
updater progress.Updater
|
updater progress.Updater
|
||||||
m sync.Mutex
|
m sync.Mutex
|
||||||
|
|
||||||
progressInfoMap map[string]progressInfoEntry
|
progressInfoMap map[string]progressInfoEntry
|
||||||
filesFinished uint64
|
s State
|
||||||
filesTotal uint64
|
|
||||||
allBytesWritten uint64
|
|
||||||
allBytesTotal uint64
|
|
||||||
started time.Time
|
started time.Time
|
||||||
|
|
||||||
printer ProgressPrinter
|
printer ProgressPrinter
|
||||||
|
@ -32,8 +36,8 @@ type term interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProgressPrinter interface {
|
type ProgressPrinter interface {
|
||||||
Update(filesFinished, filesTotal, allBytesWritten, allBytesTotal uint64, duration time.Duration)
|
Update(progress State, duration time.Duration)
|
||||||
Finish(filesFinished, filesTotal, allBytesWritten, allBytesTotal uint64, duration time.Duration)
|
Finish(progress State, duration time.Duration)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProgress(printer ProgressPrinter, interval time.Duration) *Progress {
|
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()
|
defer p.m.Unlock()
|
||||||
|
|
||||||
if !final {
|
if !final {
|
||||||
p.printer.Update(p.filesFinished, p.filesTotal, p.allBytesWritten, p.allBytesTotal, runtime)
|
p.printer.Update(p.s, runtime)
|
||||||
} else {
|
} 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()
|
p.m.Lock()
|
||||||
defer p.m.Unlock()
|
defer p.m.Unlock()
|
||||||
|
|
||||||
p.filesTotal++
|
p.s.FilesTotal++
|
||||||
p.allBytesTotal += size
|
p.s.AllBytesTotal += size
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddProgress accumulates the number of bytes written for a file
|
// 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
|
entry.bytesWritten += bytesWrittenPortion
|
||||||
p.progressInfoMap[name] = entry
|
p.progressInfoMap[name] = entry
|
||||||
|
|
||||||
p.allBytesWritten += bytesWrittenPortion
|
p.s.AllBytesWritten += bytesWrittenPortion
|
||||||
if entry.bytesWritten == entry.bytesTotal {
|
if entry.bytesWritten == entry.bytesTotal {
|
||||||
delete(p.progressInfoMap, name)
|
delete(p.progressInfoMap, name)
|
||||||
p.filesFinished++
|
p.s.FilesFinished++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type printerTraceEntry struct {
|
type printerTraceEntry struct {
|
||||||
filesFinished, filesTotal, allBytesWritten, allBytesTotal uint64
|
progress State
|
||||||
|
|
||||||
duration time.Duration
|
duration time.Duration
|
||||||
isFinished bool
|
isFinished bool
|
||||||
|
@ -22,11 +22,11 @@ type mockPrinter struct {
|
||||||
|
|
||||||
const mockFinishDuration = 42 * time.Second
|
const mockFinishDuration = 42 * time.Second
|
||||||
|
|
||||||
func (p *mockPrinter) Update(filesFinished, filesTotal, allBytesWritten, allBytesTotal uint64, duration time.Duration) {
|
func (p *mockPrinter) Update(progress State, duration time.Duration) {
|
||||||
p.trace = append(p.trace, printerTraceEntry{filesFinished, filesTotal, allBytesWritten, allBytesTotal, duration, false})
|
p.trace = append(p.trace, printerTraceEntry{progress, duration, false})
|
||||||
}
|
}
|
||||||
func (p *mockPrinter) Finish(filesFinished, filesTotal, allBytesWritten, allBytesTotal uint64, _ time.Duration) {
|
func (p *mockPrinter) Finish(progress State, _ time.Duration) {
|
||||||
p.trace = append(p.trace, printerTraceEntry{filesFinished, filesTotal, allBytesWritten, allBytesTotal, mockFinishDuration, true})
|
p.trace = append(p.trace, printerTraceEntry{progress, mockFinishDuration, true})
|
||||||
}
|
}
|
||||||
|
|
||||||
func testProgress(fn func(progress *Progress) bool) printerTrace {
|
func testProgress(fn func(progress *Progress) bool) printerTrace {
|
||||||
|
@ -45,7 +45,7 @@ func TestNew(t *testing.T) {
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
test.Equals(t, printerTrace{
|
test.Equals(t, printerTrace{
|
||||||
printerTraceEntry{0, 0, 0, 0, 0, false},
|
printerTraceEntry{State{0, 0, 0, 0}, 0, false},
|
||||||
}, result)
|
}, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ func TestAddFile(t *testing.T) {
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
test.Equals(t, printerTrace{
|
test.Equals(t, printerTrace{
|
||||||
printerTraceEntry{0, 1, 0, fileSize, 0, false},
|
printerTraceEntry{State{0, 1, 0, fileSize}, 0, false},
|
||||||
}, result)
|
}, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ func TestFirstProgressOnAFile(t *testing.T) {
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
test.Equals(t, printerTrace{
|
test.Equals(t, printerTrace{
|
||||||
printerTraceEntry{0, 1, expectedBytesWritten, expectedBytesTotal, 0, false},
|
printerTraceEntry{State{0, 1, expectedBytesWritten, expectedBytesTotal}, 0, false},
|
||||||
}, result)
|
}, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ func TestLastProgressOnAFile(t *testing.T) {
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
test.Equals(t, printerTrace{
|
test.Equals(t, printerTrace{
|
||||||
printerTraceEntry{1, 1, fileSize, fileSize, 0, false},
|
printerTraceEntry{State{1, 1, fileSize, fileSize}, 0, false},
|
||||||
}, result)
|
}, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ func TestLastProgressOnLastFile(t *testing.T) {
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
test.Equals(t, printerTrace{
|
test.Equals(t, printerTrace{
|
||||||
printerTraceEntry{2, 2, 50 + fileSize, 50 + fileSize, 0, false},
|
printerTraceEntry{State{2, 2, 50 + fileSize, 50 + fileSize}, 0, false},
|
||||||
}, result)
|
}, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ func TestSummaryOnSuccess(t *testing.T) {
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
test.Equals(t, printerTrace{
|
test.Equals(t, printerTrace{
|
||||||
printerTraceEntry{2, 2, 50 + fileSize, 50 + fileSize, mockFinishDuration, true},
|
printerTraceEntry{State{2, 2, 50 + fileSize, 50 + fileSize}, mockFinishDuration, true},
|
||||||
}, result)
|
}, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,6 +132,6 @@ func TestSummaryOnErrors(t *testing.T) {
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
test.Equals(t, printerTrace{
|
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)
|
}, result)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
timeLeft := ui.FormatDuration(duration)
|
||||||
formattedAllBytesWritten := ui.FormatBytes(allBytesWritten)
|
formattedAllBytesWritten := ui.FormatBytes(p.AllBytesWritten)
|
||||||
formattedAllBytesTotal := ui.FormatBytes(allBytesTotal)
|
formattedAllBytesTotal := ui.FormatBytes(p.AllBytesTotal)
|
||||||
allPercent := ui.FormatPercent(allBytesWritten, allBytesTotal)
|
allPercent := ui.FormatPercent(p.AllBytesWritten, p.AllBytesTotal)
|
||||||
progress := fmt.Sprintf("[%s] %s %v files/dirs %s, total %v files/dirs %v",
|
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})
|
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{})
|
t.terminal.SetStatus([]string{})
|
||||||
|
|
||||||
timeLeft := ui.FormatDuration(duration)
|
timeLeft := ui.FormatDuration(duration)
|
||||||
formattedAllBytesTotal := ui.FormatBytes(allBytesTotal)
|
formattedAllBytesTotal := ui.FormatBytes(p.AllBytesTotal)
|
||||||
|
|
||||||
var summary string
|
var summary string
|
||||||
if filesFinished == filesTotal && allBytesWritten == allBytesTotal {
|
if p.FilesFinished == p.FilesTotal && p.AllBytesWritten == p.AllBytesTotal {
|
||||||
summary = fmt.Sprintf("Summary: Restored %d files/dirs (%s) in %s", filesTotal, formattedAllBytesTotal, timeLeft)
|
summary = fmt.Sprintf("Summary: Restored %d files/dirs (%s) in %s", p.FilesTotal, formattedAllBytesTotal, timeLeft)
|
||||||
} else {
|
} else {
|
||||||
formattedAllBytesWritten := ui.FormatBytes(allBytesWritten)
|
formattedAllBytesWritten := ui.FormatBytes(p.AllBytesWritten)
|
||||||
summary = fmt.Sprintf("Summary: Restored %d / %d files/dirs (%s / %s) in %s",
|
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)
|
t.terminal.Print(summary)
|
||||||
|
|
|
@ -22,20 +22,20 @@ func (m *mockTerm) SetStatus(lines []string) {
|
||||||
func TestPrintUpdate(t *testing.T) {
|
func TestPrintUpdate(t *testing.T) {
|
||||||
term := &mockTerm{}
|
term := &mockTerm{}
|
||||||
printer := NewTextProgress(term)
|
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)
|
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) {
|
func TestPrintSummaryOnSuccess(t *testing.T) {
|
||||||
term := &mockTerm{}
|
term := &mockTerm{}
|
||||||
printer := NewTextProgress(term)
|
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)
|
test.Equals(t, []string{"Summary: Restored 11 files/dirs (47 B) in 0:05"}, term.output)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPrintSummaryOnErrors(t *testing.T) {
|
func TestPrintSummaryOnErrors(t *testing.T) {
|
||||||
term := &mockTerm{}
|
term := &mockTerm{}
|
||||||
printer := NewTextProgress(term)
|
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)
|
test.Equals(t, []string{"Summary: Restored 3 / 11 files/dirs (29 B / 47 B) in 0:05"}, term.output)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue