291 lines
7.7 KiB
Go
291 lines
7.7 KiB
Go
|
// Licensed to Elasticsearch B.V. under one or more contributor
|
||
|
// license agreements. See the NOTICE file distributed with
|
||
|
// this work for additional information regarding copyright
|
||
|
// ownership. Elasticsearch B.V. licenses this file to you under
|
||
|
// the Apache License, Version 2.0 (the "License"); you may
|
||
|
// not use this file except in compliance with the License.
|
||
|
// You may obtain a copy of the License at
|
||
|
//
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
//
|
||
|
// Unless required by applicable law or agreed to in writing,
|
||
|
// software distributed under the License is distributed on an
|
||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||
|
// KIND, either express or implied. See the License for the
|
||
|
// specific language governing permissions and limitations
|
||
|
// under the License.
|
||
|
|
||
|
package common
|
||
|
|
||
|
import (
|
||
|
"flag"
|
||
|
"strings"
|
||
|
|
||
|
ucfg "github.com/elastic/go-ucfg"
|
||
|
cfgflag "github.com/elastic/go-ucfg/flag"
|
||
|
)
|
||
|
|
||
|
// StringsFlag collects multiple usages of the same flag into an array of strings.
|
||
|
// Duplicate values will be ignored.
|
||
|
type StringsFlag struct {
|
||
|
list *[]string
|
||
|
isDefault bool
|
||
|
flag *flag.Flag
|
||
|
}
|
||
|
|
||
|
// SettingsFlag captures key/values pairs into an Config object.
|
||
|
// The flag backed by SettingsFlag can be used multiple times.
|
||
|
// Values are overwritten by the last usage of a key.
|
||
|
type SettingsFlag cfgflag.FlagValue
|
||
|
|
||
|
// flagOverwrite provides a flag value, which always overwrites the same setting
|
||
|
// in an Config object.
|
||
|
type flagOverwrite struct {
|
||
|
config *ucfg.Config
|
||
|
path string
|
||
|
value string
|
||
|
}
|
||
|
|
||
|
// StringArrFlag creates and registers a new StringsFlag with the given FlagSet.
|
||
|
// If no FlagSet is passed, flag.CommandLine will be used as target FlagSet.
|
||
|
func StringArrFlag(fs *flag.FlagSet, name, def, usage string) *StringsFlag {
|
||
|
var arr *[]string
|
||
|
if def != "" {
|
||
|
arr = &[]string{def}
|
||
|
} else {
|
||
|
arr = &[]string{}
|
||
|
}
|
||
|
|
||
|
return StringArrVarFlag(fs, arr, name, usage)
|
||
|
}
|
||
|
|
||
|
// StringArrVarFlag creates and registers a new StringsFlag with the given
|
||
|
// FlagSet. Results of the flag usage will be appended to `arr`. If the slice
|
||
|
// is not initially empty, its first value will be used as default. If the flag
|
||
|
// is used, the slice will be emptied first. If no FlagSet is passed,
|
||
|
// flag.CommandLine will be used as target FlagSet.
|
||
|
func StringArrVarFlag(fs *flag.FlagSet, arr *[]string, name, usage string) *StringsFlag {
|
||
|
if fs == nil {
|
||
|
fs = flag.CommandLine
|
||
|
}
|
||
|
f := NewStringsFlag(arr)
|
||
|
f.Register(fs, name, usage)
|
||
|
return f
|
||
|
}
|
||
|
|
||
|
// NewStringsFlag creates a new, but unregistered StringsFlag instance.
|
||
|
// Results of the flag usage will be appended to `arr`. If the slice is not
|
||
|
// initially empty, its first value will be used as default. If the flag is
|
||
|
// used, the slice will be emptied first.
|
||
|
func NewStringsFlag(arr *[]string) *StringsFlag {
|
||
|
if arr == nil {
|
||
|
panic("No target array")
|
||
|
}
|
||
|
return &StringsFlag{list: arr, isDefault: true}
|
||
|
}
|
||
|
|
||
|
// Register registers the StringsFlag instance with a FlagSet.
|
||
|
// A valid FlagSet must be used.
|
||
|
// Register panics if the flag is already registered.
|
||
|
func (f *StringsFlag) Register(fs *flag.FlagSet, name, usage string) {
|
||
|
if f.flag != nil {
|
||
|
panic("StringsFlag is already registered")
|
||
|
}
|
||
|
|
||
|
fs.Var(f, name, usage)
|
||
|
f.flag = fs.Lookup(name)
|
||
|
if f.flag == nil {
|
||
|
panic("Failed to lookup registered flag")
|
||
|
}
|
||
|
|
||
|
if len(*f.list) > 0 {
|
||
|
f.flag.DefValue = (*f.list)[0]
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// String joins all it's values set into a comma-separated string.
|
||
|
func (f *StringsFlag) String() string {
|
||
|
if f == nil || f.list == nil {
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
l := *f.list
|
||
|
return strings.Join(l, ", ")
|
||
|
}
|
||
|
|
||
|
// SetDefault sets the flags new default value.
|
||
|
// This overwrites the contents in the backing array.
|
||
|
func (f *StringsFlag) SetDefault(v string) {
|
||
|
if f.flag != nil {
|
||
|
f.flag.DefValue = v
|
||
|
}
|
||
|
|
||
|
*f.list = []string{v}
|
||
|
f.isDefault = true
|
||
|
}
|
||
|
|
||
|
// Set is used to pass usage of the flag to StringsFlag. Set adds the new value
|
||
|
// to the backing array. The array will be emptied on Set, if the backing array
|
||
|
// still contains the default value.
|
||
|
func (f *StringsFlag) Set(v string) error {
|
||
|
// Ignore duplicates, can be caused by multiple flag parses
|
||
|
if f.isDefault {
|
||
|
*f.list = []string{v}
|
||
|
} else {
|
||
|
for _, old := range *f.list {
|
||
|
if old == v {
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
*f.list = append(*f.list, v)
|
||
|
}
|
||
|
f.isDefault = false
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Get returns the backing slice its contents as interface{}. The type used is
|
||
|
// `[]string`.
|
||
|
func (f *StringsFlag) Get() interface{} {
|
||
|
return f.List()
|
||
|
}
|
||
|
|
||
|
// List returns the current set values.
|
||
|
func (f *StringsFlag) List() []string {
|
||
|
return *f.list
|
||
|
}
|
||
|
|
||
|
// Type reports the type of contents (string) expected to be parsed by Set.
|
||
|
// It is used to build the CLI usage string.
|
||
|
func (f *StringsFlag) Type() string {
|
||
|
return "string"
|
||
|
}
|
||
|
|
||
|
// SettingFlag defines a setting flag, name and it's usage. The return value is
|
||
|
// the Config object settings are applied to.
|
||
|
func SettingFlag(fs *flag.FlagSet, name, usage string) *Config {
|
||
|
cfg := NewConfig()
|
||
|
SettingVarFlag(fs, cfg, name, usage)
|
||
|
return cfg
|
||
|
}
|
||
|
|
||
|
// SettingVarFlag defines a setting flag, name and it's usage.
|
||
|
// Settings are applied to the Config object passed.
|
||
|
func SettingVarFlag(fs *flag.FlagSet, def *Config, name, usage string) {
|
||
|
if fs == nil {
|
||
|
fs = flag.CommandLine
|
||
|
}
|
||
|
|
||
|
f := NewSettingsFlag(def)
|
||
|
fs.Var(f, name, usage)
|
||
|
}
|
||
|
|
||
|
// NewSettingsFlag creates a new SettingsFlag instance, not registered with any
|
||
|
// FlagSet.
|
||
|
func NewSettingsFlag(def *Config) *SettingsFlag {
|
||
|
opts := append(
|
||
|
[]ucfg.Option{
|
||
|
ucfg.MetaData(ucfg.Meta{Source: "command line flag"}),
|
||
|
},
|
||
|
configOpts...,
|
||
|
)
|
||
|
|
||
|
tmp := cfgflag.NewFlagKeyValue(def.access(), true, opts...)
|
||
|
return (*SettingsFlag)(tmp)
|
||
|
}
|
||
|
|
||
|
func (f *SettingsFlag) access() *cfgflag.FlagValue {
|
||
|
return (*cfgflag.FlagValue)(f)
|
||
|
}
|
||
|
|
||
|
// Config returns the config object the SettingsFlag stores applied settings to.
|
||
|
func (f *SettingsFlag) Config() *Config {
|
||
|
return fromConfig(f.access().Config())
|
||
|
}
|
||
|
|
||
|
// Set sets a settings value in the Config object. The input string must be a
|
||
|
// key-value pair like `key=value`. If the value is missing, the value is set
|
||
|
// to the boolean value `true`.
|
||
|
func (f *SettingsFlag) Set(s string) error {
|
||
|
return f.access().Set(s)
|
||
|
}
|
||
|
|
||
|
// Get returns the Config object used to store values.
|
||
|
func (f *SettingsFlag) Get() interface{} {
|
||
|
return f.Config()
|
||
|
}
|
||
|
|
||
|
// String always returns an empty string. It is required to fulfil
|
||
|
// the flag.Value interface.
|
||
|
func (f *SettingsFlag) String() string {
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
// Type reports the type of contents (setting=value) expected to be parsed by Set.
|
||
|
// It is used to build the CLI usage string.
|
||
|
func (f *SettingsFlag) Type() string {
|
||
|
return "setting=value"
|
||
|
}
|
||
|
|
||
|
// ConfigOverwriteFlag defines a new flag updating a setting in an Config
|
||
|
// object. The name is used as the flag its name the path parameter is the
|
||
|
// full setting name to be used when the flag is set.
|
||
|
func ConfigOverwriteFlag(
|
||
|
fs *flag.FlagSet,
|
||
|
config *Config,
|
||
|
name, path, def, usage string,
|
||
|
) *string {
|
||
|
if config == nil {
|
||
|
panic("Missing configuration")
|
||
|
}
|
||
|
if path == "" {
|
||
|
panic("empty path")
|
||
|
}
|
||
|
|
||
|
if fs == nil {
|
||
|
fs = flag.CommandLine
|
||
|
}
|
||
|
|
||
|
if def != "" {
|
||
|
err := config.SetString(path, -1, def)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
f := newOverwriteFlag(config, path, def)
|
||
|
fs.Var(f, name, usage)
|
||
|
return &f.value
|
||
|
}
|
||
|
|
||
|
func newOverwriteFlag(config *Config, path, def string) *flagOverwrite {
|
||
|
return &flagOverwrite{config: config.access(), path: path, value: def}
|
||
|
}
|
||
|
|
||
|
func (f *flagOverwrite) String() string {
|
||
|
return f.value
|
||
|
}
|
||
|
|
||
|
func (f *flagOverwrite) Set(v string) error {
|
||
|
opts := append(
|
||
|
[]ucfg.Option{
|
||
|
ucfg.MetaData(ucfg.Meta{Source: "command line flag"}),
|
||
|
},
|
||
|
configOpts...,
|
||
|
)
|
||
|
|
||
|
err := f.config.SetString(f.path, -1, v, opts...)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
f.value = v
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (f *flagOverwrite) Get() interface{} {
|
||
|
return f.value
|
||
|
}
|
||
|
|
||
|
func (f *flagOverwrite) Type() string {
|
||
|
return "string"
|
||
|
}
|