forked from TrueCloudLab/restic
options: Allow registering
This commit is contained in:
parent
541484d142
commit
b7671dafc8
2 changed files with 167 additions and 0 deletions
|
@ -3,6 +3,7 @@ package options
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"restic/errors"
|
"restic/errors"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -11,6 +12,80 @@ import (
|
||||||
// Options holds options in the form key=value.
|
// Options holds options in the form key=value.
|
||||||
type Options map[string]string
|
type Options map[string]string
|
||||||
|
|
||||||
|
var opts []Help
|
||||||
|
|
||||||
|
// Register allows registering options so that they can be listed with List.
|
||||||
|
func Register(ns string, cfg interface{}) {
|
||||||
|
opts = appendAllOptions(opts, ns, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// List returns a list of all registered options (using Register()).
|
||||||
|
func List() (list []Help) {
|
||||||
|
list = make([]Help, 0, len(opts))
|
||||||
|
for _, opt := range opts {
|
||||||
|
list = append(list, opt)
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendAllOptions appends all options in cfg to opts, sorted by namespace.
|
||||||
|
func appendAllOptions(opts []Help, ns string, cfg interface{}) []Help {
|
||||||
|
for _, opt := range listOptions(cfg) {
|
||||||
|
opt.Namespace = ns
|
||||||
|
opts = append(opts, opt)
|
||||||
|
}
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
// listOptions returns a list of options of cfg.
|
||||||
|
func listOptions(cfg interface{}) (opts []Help) {
|
||||||
|
// resolve indirection if cfg is a pointer
|
||||||
|
v := reflect.Indirect(reflect.ValueOf(cfg))
|
||||||
|
|
||||||
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
f := v.Type().Field(i)
|
||||||
|
|
||||||
|
h := Help{
|
||||||
|
Name: f.Tag.Get("option"),
|
||||||
|
Text: f.Tag.Get("help"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if h.Name == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
opts = append(opts, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(helpList(opts))
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
// Help contains information about an option.
|
||||||
|
type Help struct {
|
||||||
|
Namespace string
|
||||||
|
Name string
|
||||||
|
Text string
|
||||||
|
}
|
||||||
|
|
||||||
|
type helpList []Help
|
||||||
|
|
||||||
|
// Len is the number of elements in the collection.
|
||||||
|
func (h helpList) Len() int {
|
||||||
|
return len(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Less reports whether the element with
|
||||||
|
// index i should sort before the element with index j.
|
||||||
|
func (h helpList) Less(i, j int) bool {
|
||||||
|
return h[i].Namespace < h[j].Namespace
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap swaps the elements with indexes i and j.
|
||||||
|
func (h helpList) Swap(i, j int) {
|
||||||
|
h[i], h[j] = h[j], h[i]
|
||||||
|
}
|
||||||
|
|
||||||
// splitKeyValue splits at the first equals (=) sign.
|
// splitKeyValue splits at the first equals (=) sign.
|
||||||
func splitKeyValue(s string) (key string, value string) {
|
func splitKeyValue(s string) (key string, value string) {
|
||||||
data := strings.SplitN(s, "=", 2)
|
data := strings.SplitN(s, "=", 2)
|
||||||
|
|
|
@ -218,3 +218,95 @@ func TestOptionsApplyInvalid(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestListOptions(t *testing.T) {
|
||||||
|
var teststruct = struct {
|
||||||
|
Foo string `option:"foo" help:"bar text help"`
|
||||||
|
}{}
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
cfg interface{}
|
||||||
|
opts []Help
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
struct {
|
||||||
|
Foo string `option:"foo" help:"bar text help"`
|
||||||
|
}{},
|
||||||
|
[]Help{
|
||||||
|
Help{Name: "foo", Text: "bar text help"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
struct {
|
||||||
|
Foo string `option:"foo" help:"bar text help"`
|
||||||
|
Bar string `option:"bar" help:"bar text help"`
|
||||||
|
}{},
|
||||||
|
[]Help{
|
||||||
|
Help{Name: "foo", Text: "bar text help"},
|
||||||
|
Help{Name: "bar", Text: "bar text help"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
struct {
|
||||||
|
Bar string `option:"bar" help:"bar text help"`
|
||||||
|
Foo string `option:"foo" help:"bar text help"`
|
||||||
|
}{},
|
||||||
|
[]Help{
|
||||||
|
Help{Name: "bar", Text: "bar text help"},
|
||||||
|
Help{Name: "foo", Text: "bar text help"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&teststruct,
|
||||||
|
[]Help{
|
||||||
|
Help{Name: "foo", Text: "bar text help"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run("", func(t *testing.T) {
|
||||||
|
opts := listOptions(test.cfg)
|
||||||
|
if !reflect.DeepEqual(opts, test.opts) {
|
||||||
|
t.Fatalf("wrong opts, want:\n %v\ngot:\n %v", test.opts, opts)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAppendAllOptions(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
cfgs map[string]interface{}
|
||||||
|
opts []Help
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
map[string]interface{}{
|
||||||
|
"local": struct {
|
||||||
|
Foo string `option:"foo" help:"bar text help"`
|
||||||
|
}{},
|
||||||
|
"sftp": struct {
|
||||||
|
Foo string `option:"foo" help:"bar text help2"`
|
||||||
|
Bar string `option:"bar" help:"bar text help"`
|
||||||
|
}{},
|
||||||
|
},
|
||||||
|
[]Help{
|
||||||
|
Help{Namespace: "local", Name: "foo", Text: "bar text help"},
|
||||||
|
Help{Namespace: "sftp", Name: "foo", Text: "bar text help2"},
|
||||||
|
Help{Namespace: "sftp", Name: "bar", Text: "bar text help"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run("", func(t *testing.T) {
|
||||||
|
var opts []Help
|
||||||
|
for ns, cfg := range test.cfgs {
|
||||||
|
opts = appendAllOptions(opts, ns, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(opts, test.opts) {
|
||||||
|
t.Fatalf("wrong list, want:\n %v\ngot:\n %v", test.opts, opts)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue