mirror of
https://github.com/restic/restic.git
synced 2025-08-23 09:47:42 +00:00
Moves files
This commit is contained in:
217
internal/options/options.go
Normal file
217
internal/options/options.go
Normal file
@@ -0,0 +1,217 @@
|
||||
package options
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"restic/errors"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
sort.Sort(helpList(opts))
|
||||
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)
|
||||
}
|
||||
|
||||
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 {
|
||||
if h[i].Namespace == h[j].Namespace {
|
||||
return h[i].Name < h[j].Name
|
||||
}
|
||||
|
||||
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)
|
||||
key = strings.ToLower(strings.TrimSpace(data[0]))
|
||||
if len(data) == 1 {
|
||||
// no equals sign is treated as the empty value
|
||||
return key, ""
|
||||
}
|
||||
|
||||
return key, strings.TrimSpace(data[1])
|
||||
}
|
||||
|
||||
// Parse takes a slice of key=value pairs and returns an Options type.
|
||||
// The key may include namespaces, separated by dots. Example: "foo.bar=value".
|
||||
// Keys are converted to lower-case.
|
||||
func Parse(in []string) (Options, error) {
|
||||
opts := make(Options, len(in))
|
||||
|
||||
for _, opt := range in {
|
||||
key, value := splitKeyValue(opt)
|
||||
|
||||
if key == "" {
|
||||
return Options{}, errors.Fatalf("empty key is not a valid option")
|
||||
}
|
||||
|
||||
if v, ok := opts[key]; ok && v != value {
|
||||
return Options{}, errors.Fatalf("key %q present more than once", key)
|
||||
}
|
||||
|
||||
opts[key] = value
|
||||
}
|
||||
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
// Extract returns an Options type with all keys in namespace ns, which is
|
||||
// also stripped from the keys. ns must end with a dot.
|
||||
func (o Options) Extract(ns string) Options {
|
||||
l := len(ns)
|
||||
if ns[l-1] != '.' {
|
||||
ns += "."
|
||||
l++
|
||||
}
|
||||
|
||||
opts := make(Options)
|
||||
|
||||
for k, v := range o {
|
||||
if !strings.HasPrefix(k, ns) {
|
||||
continue
|
||||
}
|
||||
|
||||
opts[k[l:]] = v
|
||||
}
|
||||
|
||||
return opts
|
||||
}
|
||||
|
||||
// Apply sets the options on dst via reflection, using the struct tag `option`.
|
||||
// The namespace argument (ns) is only used for error messages.
|
||||
func (o Options) Apply(ns string, dst interface{}) error {
|
||||
v := reflect.ValueOf(dst).Elem()
|
||||
|
||||
fields := make(map[string]reflect.StructField)
|
||||
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
f := v.Type().Field(i)
|
||||
tag := f.Tag.Get("option")
|
||||
|
||||
if tag == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := fields[tag]; ok {
|
||||
panic("option tag " + tag + " is not unique in " + v.Type().Name())
|
||||
}
|
||||
|
||||
fields[tag] = f
|
||||
}
|
||||
|
||||
for key, value := range o {
|
||||
field, ok := fields[key]
|
||||
if !ok {
|
||||
if ns != "" {
|
||||
key = ns + "." + key
|
||||
}
|
||||
return errors.Fatalf("option %v is not known", key)
|
||||
}
|
||||
|
||||
i := field.Index[0]
|
||||
switch v.Type().Field(i).Type.Name() {
|
||||
case "string":
|
||||
v.Field(i).SetString(value)
|
||||
|
||||
case "int":
|
||||
vi, err := strconv.ParseInt(value, 0, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v.Field(i).SetInt(vi)
|
||||
|
||||
case "uint":
|
||||
vi, err := strconv.ParseUint(value, 0, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v.Field(i).SetUint(vi)
|
||||
|
||||
case "Duration":
|
||||
d, err := time.ParseDuration(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v.Field(i).SetInt(int64(d))
|
||||
|
||||
default:
|
||||
panic("type " + v.Type().Field(i).Type.Name() + " not handled")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user