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 (
|
||||
"reflect"
|
||||
"restic/errors"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -11,6 +12,80 @@ import (
|
|||
// Options holds options in the form key=value.
|
||||
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.
|
||||
func splitKeyValue(s string) (key string, value string) {
|
||||
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