From e753941ad3d5a67ae539a04148e7bd9238ee68f8 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Sun, 14 Sep 2025 19:42:33 +0200 Subject: [PATCH] move NewProgressPrinter to ui package --- cmd/restic/cmd_backup.go | 6 +- cmd/restic/cmd_cache.go | 2 +- cmd/restic/cmd_cat.go | 6 +- cmd/restic/cmd_check.go | 4 +- cmd/restic/cmd_copy.go | 6 +- cmd/restic/cmd_debug.go | 6 +- cmd/restic/cmd_diff.go | 4 +- cmd/restic/cmd_dump.go | 4 +- cmd/restic/cmd_find.go | 4 +- cmd/restic/cmd_forget.go | 2 +- cmd/restic/cmd_generate.go | 2 +- cmd/restic/cmd_init.go | 2 +- cmd/restic/cmd_key_add.go | 2 +- cmd/restic/cmd_key_list.go | 2 +- cmd/restic/cmd_key_passwd.go | 2 +- cmd/restic/cmd_key_remove.go | 2 +- cmd/restic/cmd_list.go | 2 +- cmd/restic/cmd_ls.go | 4 +- cmd/restic/cmd_migrate.go | 2 +- cmd/restic/cmd_mount.go | 4 +- cmd/restic/cmd_mount_integration_test.go | 3 +- cmd/restic/cmd_prune.go | 4 +- cmd/restic/cmd_recover.go | 4 +- cmd/restic/cmd_repair_index.go | 2 +- cmd/restic/cmd_repair_packs.go | 4 +- cmd/restic/cmd_repair_snapshots.go | 4 +- cmd/restic/cmd_restore.go | 6 +- cmd/restic/cmd_rewrite.go | 4 +- cmd/restic/cmd_rewrite_integration_test.go | 6 +- cmd/restic/cmd_self_update.go | 2 +- cmd/restic/cmd_snapshots.go | 2 +- cmd/restic/cmd_stats.go | 4 +- cmd/restic/cmd_tag.go | 2 +- cmd/restic/cmd_unlock.go | 2 +- cmd/restic/cmd_version.go | 3 +- cmd/restic/integration_helpers_test.go | 11 +-- cmd/restic/integration_test.go | 3 +- cmd/restic/progress.go | 83 ---------------------- internal/ui/progress.go | 82 +++++++++++++++++++++ 39 files changed, 151 insertions(+), 148 deletions(-) delete mode 100644 cmd/restic/progress.go create mode 100644 internal/ui/progress.go diff --git a/cmd/restic/cmd_backup.go b/cmd/restic/cmd_backup.go index 8c070b660..a69a4b46a 100644 --- a/cmd/restic/cmd_backup.go +++ b/cmd/restic/cmd_backup.go @@ -478,7 +478,7 @@ func runBackup(ctx context.Context, opts BackupOptions, gopts GlobalOptions, ter var vsscfg fs.VSSConfig var err error - msg := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term) + msg := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) if runtime.GOOS == "windows" { if vsscfg, err = fs.ParseVSSConfig(gopts.extended); err != nil { return err @@ -521,7 +521,7 @@ func runBackup(ctx context.Context, opts BackupOptions, gopts GlobalOptions, ter progressPrinter = backup.NewTextProgress(term, gopts.verbosity) } progressReporter := backup.NewProgress(progressPrinter, - calculateProgressInterval(!gopts.Quiet, gopts.JSON, term.CanUpdateStatus())) + ui.CalculateProgressInterval(!gopts.Quiet, gopts.JSON, term.CanUpdateStatus())) defer progressReporter.Done() // rejectByNameFuncs collect functions that can reject items from the backup based on path only @@ -550,7 +550,7 @@ func runBackup(ctx context.Context, opts BackupOptions, gopts GlobalOptions, ter progressPrinter.V("load index files") } - bar := newIndexTerminalProgress(msg) + bar := ui.NewIndexCounter(msg) err = repo.LoadIndex(ctx, bar) if err != nil { return err diff --git a/cmd/restic/cmd_cache.go b/cmd/restic/cmd_cache.go index d61992f43..0640716cd 100644 --- a/cmd/restic/cmd_cache.go +++ b/cmd/restic/cmd_cache.go @@ -56,7 +56,7 @@ func (opts *CacheOptions) AddFlags(f *pflag.FlagSet) { } func runCache(opts CacheOptions, gopts GlobalOptions, args []string, term ui.Terminal) error { - printer := newTerminalProgressPrinter(false, gopts.verbosity, term) + printer := ui.NewProgressPrinter(false, gopts.verbosity, term) if len(args) > 0 { return errors.Fatal("the cache command expects no arguments, only options - please see `restic help cache` for usage and flags") diff --git a/cmd/restic/cmd_cat.go b/cmd/restic/cmd_cat.go index cca356740..3c3573d66 100644 --- a/cmd/restic/cmd_cat.go +++ b/cmd/restic/cmd_cat.go @@ -65,7 +65,7 @@ func validateCatArgs(args []string) error { } func runCat(ctx context.Context, gopts GlobalOptions, args []string, term ui.Terminal) error { - printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) if err := validateCatArgs(args); err != nil { return err @@ -168,7 +168,7 @@ func runCat(ctx context.Context, gopts GlobalOptions, args []string, term ui.Ter return err case "blob": - bar := newIndexTerminalProgress(printer) + bar := ui.NewIndexCounter(printer) err = repo.LoadIndex(ctx, bar) if err != nil { return err @@ -196,7 +196,7 @@ func runCat(ctx context.Context, gopts GlobalOptions, args []string, term ui.Ter return errors.Fatalf("could not find snapshot: %v", err) } - bar := newIndexTerminalProgress(printer) + bar := ui.NewIndexCounter(printer) err = repo.LoadIndex(ctx, bar) if err != nil { return err diff --git a/cmd/restic/cmd_check.go b/cmd/restic/cmd_check.go index 04789dd4e..9897924e4 100644 --- a/cmd/restic/cmd_check.go +++ b/cmd/restic/cmd_check.go @@ -225,7 +225,7 @@ func runCheck(ctx context.Context, opts CheckOptions, gopts GlobalOptions, args var printer progress.Printer if !gopts.JSON { - printer = newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term) + printer = ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) } else { printer = newJSONErrorPrinter(term) } @@ -249,7 +249,7 @@ func runCheck(ctx context.Context, opts CheckOptions, gopts GlobalOptions, args } printer.P("load indexes\n") - bar := newIndexTerminalProgress(printer) + bar := ui.NewIndexCounter(printer) hints, errs := chkr.LoadIndex(ctx, bar) if ctx.Err() != nil { return summary, ctx.Err() diff --git a/cmd/restic/cmd_copy.go b/cmd/restic/cmd_copy.go index ade86668c..cd627af3a 100644 --- a/cmd/restic/cmd_copy.go +++ b/cmd/restic/cmd_copy.go @@ -68,7 +68,7 @@ func (opts *CopyOptions) AddFlags(f *pflag.FlagSet) { } func runCopy(ctx context.Context, opts CopyOptions, gopts GlobalOptions, args []string, term ui.Terminal) error { - printer := newTerminalProgressPrinter(false, gopts.verbosity, term) + printer := ui.NewProgressPrinter(false, gopts.verbosity, term) secondaryGopts, isFromRepo, err := fillSecondaryGlobalOpts(ctx, opts.secondaryRepoOptions, gopts, "destination", printer) if err != nil { return err @@ -101,11 +101,11 @@ func runCopy(ctx context.Context, opts CopyOptions, gopts GlobalOptions, args [] } debug.Log("Loading source index") - bar := newIndexTerminalProgress(printer) + bar := ui.NewIndexCounter(printer) if err := srcRepo.LoadIndex(ctx, bar); err != nil { return err } - bar = newIndexTerminalProgress(printer) + bar = ui.NewIndexCounter(printer) debug.Log("Loading destination index") if err := dstRepo.LoadIndex(ctx, bar); err != nil { return err diff --git a/cmd/restic/cmd_debug.go b/cmd/restic/cmd_debug.go index 27041cf57..a168327eb 100644 --- a/cmd/restic/cmd_debug.go +++ b/cmd/restic/cmd_debug.go @@ -184,7 +184,7 @@ func dumpIndexes(ctx context.Context, repo restic.ListerLoaderUnpacked, wr io.Wr } func runDebugDump(ctx context.Context, gopts GlobalOptions, args []string, term ui.Terminal) error { - printer := newTerminalProgressPrinter(false, gopts.verbosity, term) + printer := ui.NewProgressPrinter(false, gopts.verbosity, term) if len(args) != 1 { return errors.Fatal("type not specified") @@ -455,7 +455,7 @@ func storePlainBlob(id restic.ID, prefix string, plain []byte, printer progress. } func runDebugExamine(ctx context.Context, gopts GlobalOptions, opts DebugExamineOptions, args []string, term ui.Terminal) error { - printer := newTerminalProgressPrinter(false, gopts.verbosity, term) + printer := ui.NewProgressPrinter(false, gopts.verbosity, term) if opts.ExtractPack && gopts.NoLock { return fmt.Errorf("--extract-pack and --no-lock are mutually exclusive") @@ -484,7 +484,7 @@ func runDebugExamine(ctx context.Context, gopts GlobalOptions, opts DebugExamine return errors.Fatal("no pack files to examine") } - bar := newIndexTerminalProgress(printer) + bar := ui.NewIndexCounter(printer) err = repo.LoadIndex(ctx, bar) if err != nil { return err diff --git a/cmd/restic/cmd_diff.go b/cmd/restic/cmd_diff.go index 0b9a4ad2a..c10f9898d 100644 --- a/cmd/restic/cmd_diff.go +++ b/cmd/restic/cmd_diff.go @@ -365,7 +365,7 @@ func runDiff(ctx context.Context, opts DiffOptions, gopts GlobalOptions, args [] return errors.Fatalf("specify two snapshot IDs") } - printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) ctx, repo, unlock, err := openWithReadLock(ctx, gopts, gopts.NoLock, printer) if err != nil { @@ -391,7 +391,7 @@ func runDiff(ctx context.Context, opts DiffOptions, gopts GlobalOptions, args [] if !gopts.JSON { printer.P("comparing snapshot %v to %v:\n\n", sn1.ID().Str(), sn2.ID().Str()) } - bar := newIndexTerminalProgress(printer) + bar := ui.NewIndexCounter(printer) if err = repo.LoadIndex(ctx, bar); err != nil { return err } diff --git a/cmd/restic/cmd_dump.go b/cmd/restic/cmd_dump.go index 522e4a65d..fcbd9cfd4 100644 --- a/cmd/restic/cmd_dump.go +++ b/cmd/restic/cmd_dump.go @@ -130,7 +130,7 @@ func runDump(ctx context.Context, opts DumpOptions, gopts GlobalOptions, args [] return errors.Fatal("no file and no snapshot ID specified") } - printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) switch opts.Archive { case "tar", "zip": @@ -160,7 +160,7 @@ func runDump(ctx context.Context, opts DumpOptions, gopts GlobalOptions, args [] return errors.Fatalf("failed to find snapshot: %v", err) } - bar := newIndexTerminalProgress(printer) + bar := ui.NewIndexCounter(printer) err = repo.LoadIndex(ctx, bar) if err != nil { return err diff --git a/cmd/restic/cmd_find.go b/cmd/restic/cmd_find.go index a1ad9668f..dc2564a57 100644 --- a/cmd/restic/cmd_find.go +++ b/cmd/restic/cmd_find.go @@ -582,7 +582,7 @@ func runFind(ctx context.Context, opts FindOptions, gopts GlobalOptions, args [] return errors.Fatal("wrong number of arguments") } - printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) var err error pat := findPattern{pattern: args} @@ -623,7 +623,7 @@ func runFind(ctx context.Context, opts FindOptions, gopts GlobalOptions, args [] if err != nil { return err } - bar := newIndexTerminalProgress(printer) + bar := ui.NewIndexCounter(printer) if err = repo.LoadIndex(ctx, bar); err != nil { return err } diff --git a/cmd/restic/cmd_forget.go b/cmd/restic/cmd_forget.go index 7a9a8105a..edb702842 100644 --- a/cmd/restic/cmd_forget.go +++ b/cmd/restic/cmd_forget.go @@ -186,7 +186,7 @@ func runForget(ctx context.Context, opts ForgetOptions, pruneOptions PruneOption return errors.Fatal("--no-lock is only applicable in combination with --dry-run for forget command") } - printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, opts.DryRun && gopts.NoLock, printer) if err != nil { return err diff --git a/cmd/restic/cmd_generate.go b/cmd/restic/cmd_generate.go index e2fdf7fc3..e35035b77 100644 --- a/cmd/restic/cmd_generate.go +++ b/cmd/restic/cmd_generate.go @@ -115,7 +115,7 @@ func runGenerate(opts generateOptions, gopts GlobalOptions, args []string, term return errors.Fatal("the generate command expects no arguments, only options - please see `restic help generate` for usage and flags") } - printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) cmdRoot := newRootCommand(&GlobalOptions{}) if opts.ManDir != "" { diff --git a/cmd/restic/cmd_init.go b/cmd/restic/cmd_init.go index e358ffd8a..ca5b5b770 100644 --- a/cmd/restic/cmd_init.go +++ b/cmd/restic/cmd_init.go @@ -60,7 +60,7 @@ func runInit(ctx context.Context, opts InitOptions, gopts GlobalOptions, args [] return errors.Fatal("the init command expects no arguments, only options - please see `restic help init` for usage and flags") } - printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) var version uint switch opts.RepositoryVersion { diff --git a/cmd/restic/cmd_key_add.go b/cmd/restic/cmd_key_add.go index 28b91dfe3..196c4a8de 100644 --- a/cmd/restic/cmd_key_add.go +++ b/cmd/restic/cmd_key_add.go @@ -59,7 +59,7 @@ func runKeyAdd(ctx context.Context, gopts GlobalOptions, opts KeyAddOptions, arg return fmt.Errorf("the key add command expects no arguments, only options - please see `restic help key add` for usage and flags") } - printer := newTerminalProgressPrinter(false, gopts.verbosity, term) + printer := ui.NewProgressPrinter(false, gopts.verbosity, term) ctx, repo, unlock, err := openWithAppendLock(ctx, gopts, false, printer) if err != nil { return err diff --git a/cmd/restic/cmd_key_list.go b/cmd/restic/cmd_key_list.go index 21eee0c79..6394da75e 100644 --- a/cmd/restic/cmd_key_list.go +++ b/cmd/restic/cmd_key_list.go @@ -45,7 +45,7 @@ func runKeyList(ctx context.Context, gopts GlobalOptions, args []string, term ui return fmt.Errorf("the key list command expects no arguments, only options - please see `restic help key list` for usage and flags") } - printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) ctx, repo, unlock, err := openWithReadLock(ctx, gopts, gopts.NoLock, printer) if err != nil { return err diff --git a/cmd/restic/cmd_key_passwd.go b/cmd/restic/cmd_key_passwd.go index 97c782989..5eacd11b3 100644 --- a/cmd/restic/cmd_key_passwd.go +++ b/cmd/restic/cmd_key_passwd.go @@ -54,7 +54,7 @@ func runKeyPasswd(ctx context.Context, gopts GlobalOptions, opts KeyPasswdOption return fmt.Errorf("the key passwd command expects no arguments, only options - please see `restic help key passwd` for usage and flags") } - printer := newTerminalProgressPrinter(false, gopts.verbosity, term) + printer := ui.NewProgressPrinter(false, gopts.verbosity, term) ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer) if err != nil { return err diff --git a/cmd/restic/cmd_key_remove.go b/cmd/restic/cmd_key_remove.go index 0e0c9704a..6ab034bd9 100644 --- a/cmd/restic/cmd_key_remove.go +++ b/cmd/restic/cmd_key_remove.go @@ -42,7 +42,7 @@ func runKeyRemove(ctx context.Context, gopts GlobalOptions, args []string, term return fmt.Errorf("key remove expects one argument as the key id") } - printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer) if err != nil { return err diff --git a/cmd/restic/cmd_list.go b/cmd/restic/cmd_list.go index 2cbfa5e72..385cc82f6 100644 --- a/cmd/restic/cmd_list.go +++ b/cmd/restic/cmd_list.go @@ -43,7 +43,7 @@ Exit status is 12 if the password is incorrect. } func runList(ctx context.Context, gopts GlobalOptions, args []string, term ui.Terminal) error { - printer := newTerminalProgressPrinter(false, gopts.verbosity, term) + printer := ui.NewProgressPrinter(false, gopts.verbosity, term) if len(args) != 1 { return errors.Fatal("type not specified") diff --git a/cmd/restic/cmd_ls.go b/cmd/restic/cmd_ls.go index 56bb0f9b6..c3c48b97f 100644 --- a/cmd/restic/cmd_ls.go +++ b/cmd/restic/cmd_ls.go @@ -302,7 +302,7 @@ type toSortOutput struct { } func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []string, term ui.Terminal) error { - termPrinter := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term) + termPrinter := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) if len(args) == 0 { return errors.Fatal("no snapshot ID specified, specify snapshot ID or use special ID 'latest'") @@ -373,7 +373,7 @@ func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []stri return err } - bar := newIndexTerminalProgress(termPrinter) + bar := ui.NewIndexCounter(termPrinter) if err = repo.LoadIndex(ctx, bar); err != nil { return err } diff --git a/cmd/restic/cmd_migrate.go b/cmd/restic/cmd_migrate.go index f7ac20f4f..bacc7f24a 100644 --- a/cmd/restic/cmd_migrate.go +++ b/cmd/restic/cmd_migrate.go @@ -134,7 +134,7 @@ func applyMigrations(ctx context.Context, opts MigrateOptions, gopts GlobalOptio } func runMigrate(ctx context.Context, opts MigrateOptions, gopts GlobalOptions, args []string, term ui.Terminal) error { - printer := newTerminalProgressPrinter(false, gopts.verbosity, term) + printer := ui.NewProgressPrinter(false, gopts.verbosity, term) ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer) if err != nil { diff --git a/cmd/restic/cmd_mount.go b/cmd/restic/cmd_mount.go index 6eb35f837..2b12b9b98 100644 --- a/cmd/restic/cmd_mount.go +++ b/cmd/restic/cmd_mount.go @@ -114,7 +114,7 @@ func (opts *MountOptions) AddFlags(f *pflag.FlagSet) { } func runMount(ctx context.Context, opts MountOptions, gopts GlobalOptions, args []string, term ui.Terminal) error { - printer := newTerminalProgressPrinter(false, gopts.verbosity, term) + printer := ui.NewProgressPrinter(false, gopts.verbosity, term) if opts.TimeTemplate == "" { return errors.Fatal("time template string cannot be empty") @@ -146,7 +146,7 @@ func runMount(ctx context.Context, opts MountOptions, gopts GlobalOptions, args } defer unlock() - bar := newIndexTerminalProgress(printer) + bar := ui.NewIndexCounter(printer) err = repo.LoadIndex(ctx, bar) if err != nil { return err diff --git a/cmd/restic/cmd_mount_integration_test.go b/cmd/restic/cmd_mount_integration_test.go index 91c014234..ffaeaac37 100644 --- a/cmd/restic/cmd_mount_integration_test.go +++ b/cmd/restic/cmd_mount_integration_test.go @@ -16,6 +16,7 @@ import ( "github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" + "github.com/restic/restic/internal/ui" ) const ( @@ -128,7 +129,7 @@ func checkSnapshots(t testing.TB, gopts GlobalOptions, mountpoint string, snapsh } err := withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error { - printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) _, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer) if err != nil { return err diff --git a/cmd/restic/cmd_prune.go b/cmd/restic/cmd_prune.go index 71e96954b..6cefc2d81 100644 --- a/cmd/restic/cmd_prune.go +++ b/cmd/restic/cmd_prune.go @@ -166,7 +166,7 @@ func runPrune(ctx context.Context, opts PruneOptions, gopts GlobalOptions, term return errors.Fatal("--no-lock is only applicable in combination with --dry-run for prune command") } - printer := newTerminalProgressPrinter(false, gopts.verbosity, term) + printer := ui.NewProgressPrinter(false, gopts.verbosity, term) ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, opts.DryRun && gopts.NoLock, printer) if err != nil { return err @@ -190,7 +190,7 @@ func runPruneWithRepo(ctx context.Context, opts PruneOptions, repo *repository.R } // loading the index before the snapshots is ok, as we use an exclusive lock here - bar := newIndexTerminalProgress(printer) + bar := ui.NewIndexCounter(printer) err := repo.LoadIndex(ctx, bar) if err != nil { return err diff --git a/cmd/restic/cmd_recover.go b/cmd/restic/cmd_recover.go index 2dcf51376..94570ae31 100644 --- a/cmd/restic/cmd_recover.go +++ b/cmd/restic/cmd_recover.go @@ -47,7 +47,7 @@ func runRecover(ctx context.Context, gopts GlobalOptions, term ui.Terminal) erro return err } - printer := newTerminalProgressPrinter(false, gopts.verbosity, term) + printer := ui.NewProgressPrinter(false, gopts.verbosity, term) ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer) if err != nil { return err @@ -66,7 +66,7 @@ func runRecover(ctx context.Context, gopts GlobalOptions, term ui.Terminal) erro } printer.P("load index files\n") - bar := newIndexTerminalProgress(printer) + bar := ui.NewIndexCounter(printer) if err = repo.LoadIndex(ctx, bar); err != nil { return err } diff --git a/cmd/restic/cmd_repair_index.go b/cmd/restic/cmd_repair_index.go index 163e68a07..bff6af46e 100644 --- a/cmd/restic/cmd_repair_index.go +++ b/cmd/restic/cmd_repair_index.go @@ -69,7 +69,7 @@ func newRebuildIndexCommand(globalOptions *GlobalOptions) *cobra.Command { } func runRebuildIndex(ctx context.Context, opts RepairIndexOptions, gopts GlobalOptions, term ui.Terminal) error { - printer := newTerminalProgressPrinter(false, gopts.verbosity, term) + printer := ui.NewProgressPrinter(false, gopts.verbosity, term) ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer) if err != nil { diff --git a/cmd/restic/cmd_repair_packs.go b/cmd/restic/cmd_repair_packs.go index 9161cdb50..237eec913 100644 --- a/cmd/restic/cmd_repair_packs.go +++ b/cmd/restic/cmd_repair_packs.go @@ -51,7 +51,7 @@ func runRepairPacks(ctx context.Context, gopts GlobalOptions, term ui.Terminal, return errors.Fatal("no ids specified") } - printer := newTerminalProgressPrinter(false, gopts.verbosity, term) + printer := ui.NewProgressPrinter(false, gopts.verbosity, term) ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer) if err != nil { @@ -59,7 +59,7 @@ func runRepairPacks(ctx context.Context, gopts GlobalOptions, term ui.Terminal, } defer unlock() - bar := newIndexTerminalProgress(printer) + bar := ui.NewIndexCounter(printer) err = repo.LoadIndex(ctx, bar) if err != nil { return errors.Fatalf("%s", err) diff --git a/cmd/restic/cmd_repair_snapshots.go b/cmd/restic/cmd_repair_snapshots.go index d109e1097..eacf7f2e5 100644 --- a/cmd/restic/cmd_repair_snapshots.go +++ b/cmd/restic/cmd_repair_snapshots.go @@ -74,7 +74,7 @@ func (opts *RepairOptions) AddFlags(f *pflag.FlagSet) { } func runRepairSnapshots(ctx context.Context, gopts GlobalOptions, opts RepairOptions, args []string, term ui.Terminal) error { - printer := newTerminalProgressPrinter(false, gopts.verbosity, term) + printer := ui.NewProgressPrinter(false, gopts.verbosity, term) ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, opts.DryRun, printer) if err != nil { @@ -87,7 +87,7 @@ func runRepairSnapshots(ctx context.Context, gopts GlobalOptions, opts RepairOpt return err } - bar := newIndexTerminalProgress(printer) + bar := ui.NewIndexCounter(printer) if err := repo.LoadIndex(ctx, bar); err != nil { return err } diff --git a/cmd/restic/cmd_restore.go b/cmd/restic/cmd_restore.go index 5fb7a65ea..f4d89db60 100644 --- a/cmd/restic/cmd_restore.go +++ b/cmd/restic/cmd_restore.go @@ -89,7 +89,7 @@ func (opts *RestoreOptions) AddFlags(f *pflag.FlagSet) { func runRestore(ctx context.Context, opts RestoreOptions, gopts GlobalOptions, term ui.Terminal, args []string) error { - msg := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term) + msg := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) excludePatternFns, err := opts.ExcludePatternOptions.CollectPatterns(msg.E) if err != nil { return err @@ -145,7 +145,7 @@ func runRestore(ctx context.Context, opts RestoreOptions, gopts GlobalOptions, return errors.Fatalf("failed to find snapshot: %v", err) } - bar := newIndexTerminalProgress(msg) + bar := ui.NewIndexCounter(msg) err = repo.LoadIndex(ctx, bar) if err != nil { return err @@ -163,7 +163,7 @@ func runRestore(ctx context.Context, opts RestoreOptions, gopts GlobalOptions, printer = restoreui.NewTextProgress(term, gopts.verbosity) } - progress := restoreui.NewProgress(printer, calculateProgressInterval(!gopts.Quiet, gopts.JSON, term.CanUpdateStatus())) + progress := restoreui.NewProgress(printer, ui.CalculateProgressInterval(!gopts.Quiet, gopts.JSON, term.CanUpdateStatus())) res := restorer.NewRestorer(repo, sn, restorer.Options{ DryRun: opts.DryRun, Sparse: opts.Sparse, diff --git a/cmd/restic/cmd_rewrite.go b/cmd/restic/cmd_rewrite.go index 26677f7a7..45a0d6a01 100644 --- a/cmd/restic/cmd_rewrite.go +++ b/cmd/restic/cmd_rewrite.go @@ -294,7 +294,7 @@ func runRewrite(ctx context.Context, opts RewriteOptions, gopts GlobalOptions, a return errors.Fatal("Nothing to do: no excludes provided and no new metadata provided") } - printer := newTerminalProgressPrinter(false, gopts.verbosity, term) + printer := ui.NewProgressPrinter(false, gopts.verbosity, term) var ( repo *repository.Repository @@ -318,7 +318,7 @@ func runRewrite(ctx context.Context, opts RewriteOptions, gopts GlobalOptions, a return err } - bar := newIndexTerminalProgress(printer) + bar := ui.NewIndexCounter(printer) if err = repo.LoadIndex(ctx, bar); err != nil { return err } diff --git a/cmd/restic/cmd_rewrite_integration_test.go b/cmd/restic/cmd_rewrite_integration_test.go index 213ef0319..37a884589 100644 --- a/cmd/restic/cmd_rewrite_integration_test.go +++ b/cmd/restic/cmd_rewrite_integration_test.go @@ -42,7 +42,7 @@ func getSnapshot(t testing.TB, snapshotID restic.ID, env *testEnvironment) *rest var snapshots []*restic.Snapshot err := withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) ctx, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer) rtest.OK(t, err) defer unlock() @@ -118,7 +118,7 @@ func testRewriteMetadata(t *testing.T, metadata snapshotMetadataArgs) { var snapshots []*restic.Snapshot err := withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) ctx, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer) rtest.OK(t, err) defer unlock() @@ -166,7 +166,7 @@ func TestRewriteSnaphotSummary(t *testing.T) { // replace snapshot by one without a summary var oldSummary *restic.SnapshotSummary err := withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) _, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer) rtest.OK(t, err) defer unlock() diff --git a/cmd/restic/cmd_self_update.go b/cmd/restic/cmd_self_update.go index 5615db808..e8cd64d48 100644 --- a/cmd/restic/cmd_self_update.go +++ b/cmd/restic/cmd_self_update.go @@ -86,7 +86,7 @@ func runSelfUpdate(ctx context.Context, opts SelfUpdateOptions, gopts GlobalOpti } } - printer := newTerminalProgressPrinter(false, gopts.verbosity, term) + printer := ui.NewProgressPrinter(false, gopts.verbosity, term) printer.P("writing restic to %v", opts.Output) v, err := selfupdate.DownloadLatestStableRelease(ctx, opts.Output, version, printer.P) diff --git a/cmd/restic/cmd_snapshots.go b/cmd/restic/cmd_snapshots.go index 7cbe06f5f..908ad90b8 100644 --- a/cmd/restic/cmd_snapshots.go +++ b/cmd/restic/cmd_snapshots.go @@ -67,7 +67,7 @@ func (opts *SnapshotOptions) AddFlags(f *pflag.FlagSet) { } func runSnapshots(ctx context.Context, opts SnapshotOptions, gopts GlobalOptions, args []string, term ui.Terminal) error { - printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) ctx, repo, unlock, err := openWithReadLock(ctx, gopts, gopts.NoLock, printer) if err != nil { return err diff --git a/cmd/restic/cmd_stats.go b/cmd/restic/cmd_stats.go index 64230be95..27a0bd012 100644 --- a/cmd/restic/cmd_stats.go +++ b/cmd/restic/cmd_stats.go @@ -99,7 +99,7 @@ func runStats(ctx context.Context, opts StatsOptions, gopts GlobalOptions, args return err } - printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) ctx, repo, unlock, err := openWithReadLock(ctx, gopts, gopts.NoLock, printer) if err != nil { @@ -111,7 +111,7 @@ func runStats(ctx context.Context, opts StatsOptions, gopts GlobalOptions, args if err != nil { return err } - bar := newIndexTerminalProgress(printer) + bar := ui.NewIndexCounter(printer) if err = repo.LoadIndex(ctx, bar); err != nil { return err } diff --git a/cmd/restic/cmd_tag.go b/cmd/restic/cmd_tag.go index b1f82acfa..46ded63a1 100644 --- a/cmd/restic/cmd_tag.go +++ b/cmd/restic/cmd_tag.go @@ -117,7 +117,7 @@ func changeTags(ctx context.Context, repo *repository.Repository, sn *restic.Sna } func runTag(ctx context.Context, opts TagOptions, gopts GlobalOptions, term ui.Terminal, args []string) error { - printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) if len(opts.SetTags) == 0 && len(opts.AddTags) == 0 && len(opts.RemoveTags) == 0 { return errors.Fatal("nothing to do!") diff --git a/cmd/restic/cmd_unlock.go b/cmd/restic/cmd_unlock.go index 096d21cac..91ce9105a 100644 --- a/cmd/restic/cmd_unlock.go +++ b/cmd/restic/cmd_unlock.go @@ -44,7 +44,7 @@ func (opts *UnlockOptions) AddFlags(f *pflag.FlagSet) { } func runUnlock(ctx context.Context, opts UnlockOptions, gopts GlobalOptions, term ui.Terminal) error { - printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, term) repo, err := OpenRepository(ctx, gopts, printer) if err != nil { return err diff --git a/cmd/restic/cmd_version.go b/cmd/restic/cmd_version.go index a32575389..7bf968d6c 100644 --- a/cmd/restic/cmd_version.go +++ b/cmd/restic/cmd_version.go @@ -4,6 +4,7 @@ import ( "encoding/json" "runtime" + "github.com/restic/restic/internal/ui" "github.com/spf13/cobra" ) @@ -23,7 +24,7 @@ Exit status is 1 if there was any error. `, DisableAutoGenTag: true, Run: func(_ *cobra.Command, _ []string) { - printer := newTerminalProgressPrinter(globalOptions.JSON, globalOptions.verbosity, globalOptions.term) + printer := ui.NewProgressPrinter(globalOptions.JSON, globalOptions.verbosity, globalOptions.term) if globalOptions.JSON { type jsonVersion struct { diff --git a/cmd/restic/integration_helpers_test.go b/cmd/restic/integration_helpers_test.go index dc8b3eda9..4724fc818 100644 --- a/cmd/restic/integration_helpers_test.go +++ b/cmd/restic/integration_helpers_test.go @@ -20,6 +20,7 @@ import ( "github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" + "github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui/termstatus" ) @@ -245,7 +246,7 @@ func testSetupBackupData(t testing.TB, env *testEnvironment) string { func listPacks(gopts GlobalOptions, t *testing.T) restic.IDSet { var packs restic.IDSet err := withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error { - printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) ctx, r, unlock, err := openWithReadLock(ctx, gopts, false, printer) rtest.OK(t, err) defer unlock() @@ -264,7 +265,7 @@ func listPacks(gopts GlobalOptions, t *testing.T) restic.IDSet { func listTreePacks(gopts GlobalOptions, t *testing.T) restic.IDSet { var treePacks restic.IDSet err := withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error { - printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) ctx, r, unlock, err := openWithReadLock(ctx, gopts, false, printer) rtest.OK(t, err) defer unlock() @@ -295,7 +296,7 @@ func captureBackend(gopts *GlobalOptions) func() backend.Backend { func removePacks(gopts GlobalOptions, t testing.TB, remove restic.IDSet) { be := captureBackend(&gopts) err := withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error { - printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) ctx, _, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer) rtest.OK(t, err) defer unlock() @@ -311,7 +312,7 @@ func removePacks(gopts GlobalOptions, t testing.TB, remove restic.IDSet) { func removePacksExcept(gopts GlobalOptions, t testing.TB, keep restic.IDSet, removeTreePacks bool) { be := captureBackend(&gopts) err := withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error { - printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) ctx, r, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer) rtest.OK(t, err) defer unlock() @@ -372,7 +373,7 @@ func lastSnapshot(old, new map[string]struct{}) (map[string]struct{}, string) { func testLoadSnapshot(t testing.TB, gopts GlobalOptions, id restic.ID) *restic.Snapshot { var snapshot *restic.Snapshot err := withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error { - printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) _, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer) rtest.OK(t, err) defer unlock() diff --git a/cmd/restic/integration_test.go b/cmd/restic/integration_test.go index c0e98e232..685304e5b 100644 --- a/cmd/restic/integration_test.go +++ b/cmd/restic/integration_test.go @@ -12,6 +12,7 @@ import ( "github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" + "github.com/restic/restic/internal/ui" ) func TestCheckRestoreNoLock(t *testing.T) { @@ -162,7 +163,7 @@ func TestFindListOnce(t *testing.T) { var snapshotIDs restic.IDSet rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error { - printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) + printer := ui.NewProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term) ctx, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer) rtest.OK(t, err) defer unlock() diff --git a/cmd/restic/progress.go b/cmd/restic/progress.go deleted file mode 100644 index f72a052ae..000000000 --- a/cmd/restic/progress.go +++ /dev/null @@ -1,83 +0,0 @@ -package main - -import ( - "fmt" - "os" - "strconv" - "time" - - "github.com/restic/restic/internal/ui" - "github.com/restic/restic/internal/ui/progress" -) - -// calculateProgressInterval returns the interval configured via RESTIC_PROGRESS_FPS -// or if unset returns an interval for 60fps on interactive terminals and 0 (=disabled) -// for non-interactive terminals or when run using the --quiet flag -func calculateProgressInterval(show bool, json bool, canUpdateStatus bool) time.Duration { - interval := time.Second / 60 - fps, err := strconv.ParseFloat(os.Getenv("RESTIC_PROGRESS_FPS"), 64) - if err == nil && fps > 0 { - if fps > 60 { - fps = 60 - } - interval = time.Duration(float64(time.Second) / fps) - } else if !json && !canUpdateStatus || !show { - interval = 0 - } - return interval -} - -// newTerminalProgressMax returns a progress.Counter that prints to terminal if provided. -func newTerminalProgressMax(show bool, max uint64, description string, term ui.Terminal) *progress.Counter { - if !show { - return nil - } - interval := calculateProgressInterval(show, false, term.CanUpdateStatus()) - - return progress.NewCounter(interval, max, func(v uint64, max uint64, d time.Duration, final bool) { - var status string - if max == 0 { - status = fmt.Sprintf("[%s] %d %s", - ui.FormatDuration(d), v, description) - } else { - status = fmt.Sprintf("[%s] %s %d / %d %s", - ui.FormatDuration(d), ui.FormatPercent(v, max), v, max, description) - } - - if final { - term.SetStatus(nil) - term.Print(status) - } else { - term.SetStatus([]string{status}) - } - }) -} - -type terminalProgressPrinter struct { - term ui.Terminal - ui.Message - show bool -} - -func (t *terminalProgressPrinter) NewCounter(description string) *progress.Counter { - return newTerminalProgressMax(t.show, 0, description, t.term) -} - -func (t *terminalProgressPrinter) NewCounterTerminalOnly(description string) *progress.Counter { - return newTerminalProgressMax(t.show && t.term.OutputIsTerminal(), 0, description, t.term) -} - -func newTerminalProgressPrinter(json bool, verbosity uint, term ui.Terminal) progress.Printer { - if json { - verbosity = 0 - } - return &terminalProgressPrinter{ - term: term, - Message: *ui.NewMessage(term, verbosity), - show: verbosity > 0, - } -} - -func newIndexTerminalProgress(printer progress.Printer) *progress.Counter { - return printer.NewCounterTerminalOnly("index files loaded") -} diff --git a/internal/ui/progress.go b/internal/ui/progress.go new file mode 100644 index 000000000..9e18da5ec --- /dev/null +++ b/internal/ui/progress.go @@ -0,0 +1,82 @@ +package ui + +import ( + "fmt" + "os" + "strconv" + "time" + + "github.com/restic/restic/internal/ui/progress" +) + +// CalculateProgressInterval returns the interval configured via RESTIC_PROGRESS_FPS +// or if unset returns an interval for 60fps on interactive terminals and 0 (=disabled) +// for non-interactive terminals or when run using the --quiet flag +func CalculateProgressInterval(show bool, json bool, canUpdateStatus bool) time.Duration { + interval := time.Second / 60 + fps, err := strconv.ParseFloat(os.Getenv("RESTIC_PROGRESS_FPS"), 64) + if err == nil && fps > 0 { + if fps > 60 { + fps = 60 + } + interval = time.Duration(float64(time.Second) / fps) + } else if !json && !canUpdateStatus || !show { + interval = 0 + } + return interval +} + +// newProgressMax returns a progress.Counter that prints to terminal if provided. +func newProgressMax(show bool, max uint64, description string, term Terminal) *progress.Counter { + if !show { + return nil + } + interval := CalculateProgressInterval(show, false, term.CanUpdateStatus()) + + return progress.NewCounter(interval, max, func(v uint64, max uint64, d time.Duration, final bool) { + var status string + if max == 0 { + status = fmt.Sprintf("[%s] %d %s", + FormatDuration(d), v, description) + } else { + status = fmt.Sprintf("[%s] %s %d / %d %s", + FormatDuration(d), FormatPercent(v, max), v, max, description) + } + + if final { + term.SetStatus(nil) + term.Print(status) + } else { + term.SetStatus([]string{status}) + } + }) +} + +type progressPrinter struct { + term Terminal + Message + show bool +} + +func (t *progressPrinter) NewCounter(description string) *progress.Counter { + return newProgressMax(t.show, 0, description, t.term) +} + +func (t *progressPrinter) NewCounterTerminalOnly(description string) *progress.Counter { + return newProgressMax(t.show && t.term.OutputIsTerminal(), 0, description, t.term) +} + +func NewProgressPrinter(json bool, verbosity uint, term Terminal) progress.Printer { + if json { + verbosity = 0 + } + return &progressPrinter{ + term: term, + Message: *NewMessage(term, verbosity), + show: verbosity > 0, + } +} + +func NewIndexCounter(printer progress.Printer) *progress.Counter { + return printer.NewCounterTerminalOnly("index files loaded") +}