forked from TrueCloudLab/restic
140 lines
2.9 KiB
Go
140 lines
2.9 KiB
Go
package feature
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
type state string
|
|
type FlagName string
|
|
|
|
const (
|
|
// Alpha features are disabled by default. They do not guarantee any backwards compatibility and may change in arbitrary ways between restic versions.
|
|
Alpha state = "alpha"
|
|
// Beta features are enabled by default. They may still change, but incompatible changes should be avoided.
|
|
Beta state = "beta"
|
|
// Stable features are always enabled
|
|
Stable state = "stable"
|
|
// Deprecated features are always disabled
|
|
Deprecated state = "deprecated"
|
|
)
|
|
|
|
type FlagDesc struct {
|
|
Type state
|
|
Description string
|
|
}
|
|
|
|
type FlagSet struct {
|
|
flags map[FlagName]*FlagDesc
|
|
enabled map[FlagName]bool
|
|
}
|
|
|
|
func New() *FlagSet {
|
|
return &FlagSet{}
|
|
}
|
|
|
|
func getDefault(phase state) bool {
|
|
switch phase {
|
|
case Alpha, Deprecated:
|
|
return false
|
|
case Beta, Stable:
|
|
return true
|
|
default:
|
|
panic("unknown feature phase")
|
|
}
|
|
}
|
|
|
|
func (f *FlagSet) SetFlags(flags map[FlagName]FlagDesc) {
|
|
f.flags = map[FlagName]*FlagDesc{}
|
|
f.enabled = map[FlagName]bool{}
|
|
|
|
for name, flag := range flags {
|
|
fcopy := flag
|
|
f.flags[name] = &fcopy
|
|
f.enabled[name] = getDefault(fcopy.Type)
|
|
}
|
|
}
|
|
|
|
func (f *FlagSet) Apply(flags string, logWarning func(string)) error {
|
|
if flags == "" {
|
|
return nil
|
|
}
|
|
|
|
selection := make(map[string]bool)
|
|
|
|
for _, flag := range strings.Split(flags, ",") {
|
|
parts := strings.SplitN(flag, "=", 2)
|
|
|
|
name := parts[0]
|
|
value := "true"
|
|
if len(parts) == 2 {
|
|
value = parts[1]
|
|
}
|
|
|
|
isEnabled, err := strconv.ParseBool(value)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse value %q for feature flag %v: %w", value, name, err)
|
|
}
|
|
|
|
selection[name] = isEnabled
|
|
}
|
|
|
|
for name, value := range selection {
|
|
fname := FlagName(name)
|
|
flag := f.flags[fname]
|
|
if flag == nil {
|
|
return fmt.Errorf("unknown feature flag %q", name)
|
|
}
|
|
|
|
switch flag.Type {
|
|
case Alpha, Beta:
|
|
f.enabled[fname] = value
|
|
case Stable:
|
|
logWarning(fmt.Sprintf("feature flag %q is always enabled and will be removed in a future release", fname))
|
|
case Deprecated:
|
|
logWarning(fmt.Sprintf("feature flag %q is always disabled and will be removed in a future release", fname))
|
|
default:
|
|
panic("unknown feature phase")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (f *FlagSet) Enabled(name FlagName) bool {
|
|
isEnabled, ok := f.enabled[name]
|
|
if !ok {
|
|
panic(fmt.Sprintf("unknown feature flag %v", name))
|
|
}
|
|
|
|
return isEnabled
|
|
}
|
|
|
|
// Help contains information about a feature.
|
|
type Help struct {
|
|
Name string
|
|
Type string
|
|
Default bool
|
|
Description string
|
|
}
|
|
|
|
func (f *FlagSet) List() []Help {
|
|
var help []Help
|
|
|
|
for name, flag := range f.flags {
|
|
help = append(help, Help{
|
|
Name: string(name),
|
|
Type: string(flag.Type),
|
|
Default: getDefault(flag.Type),
|
|
Description: flag.Description,
|
|
})
|
|
}
|
|
|
|
sort.Slice(help, func(i, j int) bool {
|
|
return strings.Compare(help[i].Name, help[j].Name) < 0
|
|
})
|
|
|
|
return help
|
|
}
|