From 78d090aea5d906996234e8a6ac03577bca347ed6 Mon Sep 17 00:00:00 2001 From: Alexander Neumann Date: Sun, 9 Jul 2017 12:45:49 +0200 Subject: [PATCH] Implement TagList and TagLists as pflag.Value --- src/cmds/restic/cmd_find.go | 8 ++--- src/cmds/restic/cmd_forget.go | 17 ++++----- src/cmds/restic/cmd_ls.go | 6 ++-- src/cmds/restic/cmd_mount.go | 6 ++-- src/cmds/restic/cmd_restore.go | 6 ++-- src/cmds/restic/cmd_snapshots.go | 6 ++-- src/cmds/restic/cmd_tag.go | 6 ++-- src/restic/snapshot_policy.go | 24 ------------- src/restic/tag_list.go | 62 ++++++++++++++++++++++++++++++++ 9 files changed, 86 insertions(+), 55 deletions(-) create mode 100644 src/restic/tag_list.go diff --git a/src/cmds/restic/cmd_find.go b/src/cmds/restic/cmd_find.go index e3b8e2784..3d98c3f8a 100644 --- a/src/cmds/restic/cmd_find.go +++ b/src/cmds/restic/cmd_find.go @@ -34,7 +34,7 @@ type FindOptions struct { ListLong bool Host string Paths []string - Tags []string + Tags restic.TagLists } var findOptions FindOptions @@ -50,7 +50,7 @@ func init() { f.BoolVarP(&findOptions.ListLong, "long", "l", false, "use a long listing format showing size and mode") f.StringVarP(&findOptions.Host, "host", "H", "", "only consider snapshots for this `host`, when no snapshot ID is given") - f.StringArrayVar(&findOptions.Tags, "tag", nil, "only consider snapshots which include the list of `tag,tag,...`, when no snapshot-ID is given") + f.Var(&findOptions.Tags, "tag", "only consider snapshots which include this `taglist`, when no snapshot-ID is given") f.StringArrayVar(&findOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path`, when no snapshot-ID is given") } @@ -290,15 +290,13 @@ func runFind(opts FindOptions, gopts GlobalOptions, args []string) error { ctx, cancel := context.WithCancel(gopts.ctx) defer cancel() - tagLists := restic.SplitTagLists(opts.Tags) - f := &Finder{ repo: repo, pat: pat, out: statefulOutput{ListLong: opts.ListLong, JSON: globalOptions.JSON}, notfound: restic.NewIDSet(), } - for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, tagLists, opts.Paths, opts.Snapshots) { + for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, opts.Snapshots) { if err = f.findInSnapshot(sn); err != nil { return err } diff --git a/src/cmds/restic/cmd_forget.go b/src/cmds/restic/cmd_forget.go index e9d96d7fc..ceb8220ae 100644 --- a/src/cmds/restic/cmd_forget.go +++ b/src/cmds/restic/cmd_forget.go @@ -31,10 +31,10 @@ type ForgetOptions struct { Weekly int Monthly int Yearly int - KeepTags []string + KeepTags restic.TagLists Host string - Tags []string + Tags restic.TagLists Paths []string GroupByTags bool @@ -55,13 +55,13 @@ func init() { f.IntVarP(&forgetOptions.Monthly, "keep-monthly", "m", 0, "keep the last `n` monthly snapshots") f.IntVarP(&forgetOptions.Yearly, "keep-yearly", "y", 0, "keep the last `n` yearly snapshots") - f.StringArrayVar(&forgetOptions.KeepTags, "keep-tag", []string{}, "keep snapshots with this `tag` (can be specified multiple times)") + f.Var(&forgetOptions.KeepTags, "keep-tag", "keep snapshots with this `taglist` (can be specified multiple times)") f.BoolVarP(&forgetOptions.GroupByTags, "group-by-tags", "G", false, "Group by host,paths,tags instead of just host,paths") // Sadly the commonly used shortcut `H` is already used. f.StringVar(&forgetOptions.Host, "host", "", "only consider snapshots with the given `host`") // Deprecated since 2017-03-07. f.StringVar(&forgetOptions.Host, "hostname", "", "only consider snapshots with the given `hostname` (deprecated)") - f.StringArrayVar(&forgetOptions.Tags, "tag", nil, "only consider snapshots which include this `tag` (can be specified multiple times)") + f.Var(&forgetOptions.Tags, "tag", "only consider snapshots which include this `taglist` (can be specified multiple times)") f.StringArrayVar(&forgetOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path` (can be specified multiple times)") f.BoolVarP(&forgetOptions.DryRun, "dry-run", "n", false, "do not delete anything, just print what would be done") @@ -92,7 +92,7 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error { ctx, cancel := context.WithCancel(gopts.ctx) defer cancel() - for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, restic.SplitTagLists(opts.Tags), opts.Paths, args) { + for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, args) { if len(args) > 0 { // When explicit snapshots args are given, remove them immediately. if !opts.DryRun { @@ -122,11 +122,6 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error { return nil } - var tagLists []restic.TagList - for _, t := range opts.KeepTags { - tagLists = append(tagLists, restic.SplitTagList(t)) - } - policy := restic.ExpirePolicy{ Last: opts.Last, Hourly: opts.Hourly, @@ -134,7 +129,7 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error { Weekly: opts.Weekly, Monthly: opts.Monthly, Yearly: opts.Yearly, - Tags: tagLists, + Tags: opts.KeepTags, } if policy.Empty() { diff --git a/src/cmds/restic/cmd_ls.go b/src/cmds/restic/cmd_ls.go index c6461e950..83acd5b27 100644 --- a/src/cmds/restic/cmd_ls.go +++ b/src/cmds/restic/cmd_ls.go @@ -28,7 +28,7 @@ The special snapshot-ID "latest" can be used to list files and directories of th type LsOptions struct { ListLong bool Host string - Tags []string + Tags restic.TagLists Paths []string } @@ -41,7 +41,7 @@ func init() { flags.BoolVarP(&lsOptions.ListLong, "long", "l", false, "use a long listing format showing size and mode") flags.StringVarP(&lsOptions.Host, "host", "H", "", "only consider snapshots for this `host`, when no snapshot ID is given") - flags.StringArrayVar(&lsOptions.Tags, "tag", nil, "only consider snapshots which include this `tag`, when no snapshot ID is given") + flags.Var(&lsOptions.Tags, "tag", "only consider snapshots which include this `taglist`, when no snapshot ID is given") flags.StringArrayVar(&lsOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path`, when no snapshot ID is given") } @@ -80,7 +80,7 @@ func runLs(opts LsOptions, gopts GlobalOptions, args []string) error { ctx, cancel := context.WithCancel(gopts.ctx) defer cancel() - for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, restic.SplitTagLists(opts.Tags), opts.Paths, args) { + for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, args) { Verbosef("snapshot %s of %v at %s):\n", sn.ID().Str(), sn.Paths, sn.Time) if err = printTree(repo, sn.Tree, string(filepath.Separator)); err != nil { diff --git a/src/cmds/restic/cmd_mount.go b/src/cmds/restic/cmd_mount.go index 43538ca20..b7872eef8 100644 --- a/src/cmds/restic/cmd_mount.go +++ b/src/cmds/restic/cmd_mount.go @@ -38,7 +38,7 @@ type MountOptions struct { AllowRoot bool AllowOther bool Host string - Tags []string + Tags restic.TagLists Paths []string } @@ -53,7 +53,7 @@ func init() { mountFlags.BoolVar(&mountOptions.AllowOther, "allow-other", false, "allow other users to access the data in the mounted directory") mountFlags.StringVarP(&mountOptions.Host, "host", "H", "", `only consider snapshots for this host`) - mountFlags.StringArrayVar(&mountOptions.Tags, "tag", nil, "only consider snapshots which include this `tag`") + mountFlags.Var(&mountOptions.Tags, "tag", "only consider snapshots which include this `taglist`") mountFlags.StringArrayVar(&mountOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path`") } @@ -104,7 +104,7 @@ func mount(opts MountOptions, gopts GlobalOptions, mountpoint string) error { cfg := fuse.Config{ OwnerIsRoot: opts.OwnerRoot, Host: opts.Host, - Tags: restic.SplitTagLists(opts.Tags), + Tags: opts.Tags, Paths: opts.Paths, } root, err := fuse.NewRoot(context.TODO(), repo, cfg) diff --git a/src/cmds/restic/cmd_restore.go b/src/cmds/restic/cmd_restore.go index 4d304ce14..f78f3010c 100644 --- a/src/cmds/restic/cmd_restore.go +++ b/src/cmds/restic/cmd_restore.go @@ -31,7 +31,7 @@ type RestoreOptions struct { Target string Host string Paths []string - Tags []string + Tags restic.TagLists } var restoreOptions RestoreOptions @@ -45,7 +45,7 @@ func init() { flags.StringVarP(&restoreOptions.Target, "target", "t", "", "directory to extract data to") flags.StringVarP(&restoreOptions.Host, "host", "H", "", `only consider snapshots for this host when the snapshot ID is "latest"`) - flags.StringArrayVar(&restoreOptions.Tags, "tag", nil, "only consider snapshots which include this `tag` for snapshot ID \"latest\"") + flags.Var(&restoreOptions.Tags, "tag", "only consider snapshots which include this `taglist` for snapshot ID \"latest\"") flags.StringArrayVar(&restoreOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path` for snapshot ID \"latest\"") } @@ -89,7 +89,7 @@ func runRestore(opts RestoreOptions, gopts GlobalOptions, args []string) error { var id restic.ID if snapshotIDString == "latest" { - id, err = restic.FindLatestSnapshot(ctx, repo, opts.Paths, restic.SplitTagLists(opts.Tags), opts.Host) + id, err = restic.FindLatestSnapshot(ctx, repo, opts.Paths, opts.Tags, opts.Host) if err != nil { Exitf(1, "latest snapshot for criteria not found: %v Paths:%v Host:%v", err, opts.Paths, opts.Host) } diff --git a/src/cmds/restic/cmd_snapshots.go b/src/cmds/restic/cmd_snapshots.go index c69df0810..8a4c63c2e 100644 --- a/src/cmds/restic/cmd_snapshots.go +++ b/src/cmds/restic/cmd_snapshots.go @@ -26,7 +26,7 @@ The "snapshots" command lists all snapshots stored in the repository. // SnapshotOptions bundles all options for the snapshots command. type SnapshotOptions struct { Host string - Tags []string + Tags restic.TagLists Paths []string } @@ -37,7 +37,7 @@ func init() { f := cmdSnapshots.Flags() f.StringVarP(&snapshotOptions.Host, "host", "H", "", "only consider snapshots for this `host`") - f.StringArrayVar(&snapshotOptions.Tags, "tag", nil, "only consider snapshots which include this `tag` (can be specified multiple times)") + f.Var(&snapshotOptions.Tags, "tag", "only consider snapshots which include this `taglist` (can be specified multiple times)") f.StringArrayVar(&snapshotOptions.Paths, "path", nil, "only consider snapshots for this `path` (can be specified multiple times)") } @@ -59,7 +59,7 @@ func runSnapshots(opts SnapshotOptions, gopts GlobalOptions, args []string) erro defer cancel() var list restic.Snapshots - for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, restic.SplitTagLists(opts.Tags), opts.Paths, args) { + for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, args) { list = append(list, sn) } sort.Sort(sort.Reverse(list)) diff --git a/src/cmds/restic/cmd_tag.go b/src/cmds/restic/cmd_tag.go index b2f2dec38..014ade697 100644 --- a/src/cmds/restic/cmd_tag.go +++ b/src/cmds/restic/cmd_tag.go @@ -31,7 +31,7 @@ When no snapshot-ID is given, all snapshots matching the host, tag and path filt type TagOptions struct { Host string Paths []string - Tags []string + Tags restic.TagLists SetTags []string AddTags []string RemoveTags []string @@ -48,7 +48,7 @@ func init() { tagFlags.StringSliceVar(&tagOptions.RemoveTags, "remove", nil, "`tag` which will be removed from the existing tags (can be given multiple times)") tagFlags.StringVarP(&tagOptions.Host, "host", "H", "", "only consider snapshots for this `host`, when no snapshot ID is given") - tagFlags.StringArrayVar(&tagOptions.Tags, "tag", nil, "only consider snapshots which include this `tag`, when no snapshot-ID is given") + tagFlags.Var(&tagOptions.Tags, "tag", "only consider snapshots which include this `taglist`, when no snapshot-ID is given") tagFlags.StringArrayVar(&tagOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path`, when no snapshot-ID is given") } @@ -123,7 +123,7 @@ func runTag(opts TagOptions, gopts GlobalOptions, args []string) error { changeCnt := 0 ctx, cancel := context.WithCancel(gopts.ctx) defer cancel() - for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, restic.SplitTagLists(opts.Tags), opts.Paths, args) { + for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, args) { changed, err := changeTags(repo, sn, opts.SetTags, opts.AddTags, opts.RemoveTags) if err != nil { Warnf("unable to modify the tags for snapshot ID %q, ignoring: %v\n", sn.ID(), err) diff --git a/src/restic/snapshot_policy.go b/src/restic/snapshot_policy.go index 88ec9ed99..0775f4024 100644 --- a/src/restic/snapshot_policy.go +++ b/src/restic/snapshot_policy.go @@ -3,33 +3,9 @@ package restic import ( "reflect" "sort" - "strings" "time" ) -// TagList is a list of tags. -type TagList []string - -// SplitTagList splits a string into a list of tags. The tags in the string -// need to be separated by commas. Whitespace is stripped around the individual -// tags. -func SplitTagList(s string) (l TagList) { - for _, t := range strings.Split(s, ",") { - l = append(l, strings.TrimSpace(t)) - } - return l -} - -// SplitTagLists splits a slice of strings into a slice of TagLists using -// SplitTagList. -func SplitTagLists(s []string) (l []TagList) { - l = make([]TagList, 0, len(s)) - for _, t := range s { - l = append(l, SplitTagList(t)) - } - return l -} - // ExpirePolicy configures which snapshots should be automatically removed. type ExpirePolicy struct { Last int // keep the last n snapshots diff --git a/src/restic/tag_list.go b/src/restic/tag_list.go new file mode 100644 index 000000000..8e8f5b9f7 --- /dev/null +++ b/src/restic/tag_list.go @@ -0,0 +1,62 @@ +package restic + +import ( + "fmt" + "strings" +) + +// TagList is a list of tags. +type TagList []string + +// SplitTagList splits a string into a list of tags. The tags in the string +// need to be separated by commas. Whitespace is stripped around the individual +// tags. +func SplitTagList(s string) (l TagList) { + for _, t := range strings.Split(s, ",") { + l = append(l, strings.TrimSpace(t)) + } + return l +} + +func (l TagList) String() string { + return "[" + strings.Join(l, ", ") + "]" +} + +// Set updates the TagList's value. +func (l *TagList) Set(s string) error { + *l = SplitTagList(s) + return nil +} + +// Type returns a description of the type. +func (TagList) Type() string { + return "TagList" +} + +// TagLists consists of several TagList. +type TagLists []TagList + +// SplitTagLists splits a slice of strings into a slice of TagLists using +// SplitTagList. +func SplitTagLists(s []string) (l TagLists) { + l = make([]TagList, 0, len(s)) + for _, t := range s { + l = append(l, SplitTagList(t)) + } + return l +} + +func (l TagLists) String() string { + return fmt.Sprintf("%v", []TagList(l)) +} + +// Set updates the TagList's value. +func (l *TagLists) Set(s string) error { + *l = append(*l, SplitTagList(s)) + return nil +} + +// Type returns a description of the type. +func (TagLists) Type() string { + return "TagLists" +}