117 lines
2.7 KiB
Go
117 lines
2.7 KiB
Go
package cli
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"reflect"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
// MapBase wraps map[string]T to satisfy flag.Value
|
|
type MapBase[T any, C any, VC ValueCreator[T, C]] struct {
|
|
dict *map[string]T
|
|
hasBeenSet bool
|
|
value Value
|
|
}
|
|
|
|
func (i MapBase[T, C, VC]) Create(val map[string]T, p *map[string]T, c C) Value {
|
|
*p = map[string]T{}
|
|
for k, v := range val {
|
|
(*p)[k] = v
|
|
}
|
|
var t T
|
|
np := new(T)
|
|
var vc VC
|
|
return &MapBase[T, C, VC]{
|
|
dict: p,
|
|
value: vc.Create(t, np, c),
|
|
}
|
|
}
|
|
|
|
// NewMapBase makes a *MapBase with default values
|
|
func NewMapBase[T any, C any, VC ValueCreator[T, C]](defaults map[string]T) *MapBase[T, C, VC] {
|
|
return &MapBase[T, C, VC]{
|
|
dict: &defaults,
|
|
}
|
|
}
|
|
|
|
// Set parses the value and appends it to the list of values
|
|
func (i *MapBase[T, C, VC]) Set(value string) error {
|
|
if !i.hasBeenSet {
|
|
*i.dict = map[string]T{}
|
|
i.hasBeenSet = true
|
|
}
|
|
|
|
if strings.HasPrefix(value, slPfx) {
|
|
// Deserializing assumes overwrite
|
|
_ = json.Unmarshal([]byte(strings.Replace(value, slPfx, "", 1)), &i.dict)
|
|
i.hasBeenSet = true
|
|
return nil
|
|
}
|
|
|
|
for _, item := range flagSplitMultiValues(value) {
|
|
key, value, ok := strings.Cut(item, defaultMapFlagKeyValueSeparator)
|
|
if !ok {
|
|
return fmt.Errorf("item %q is missing separator %q", item, defaultMapFlagKeyValueSeparator)
|
|
}
|
|
if err := i.value.Set(value); err != nil {
|
|
return err
|
|
}
|
|
tmp, ok := i.value.Get().(T)
|
|
if !ok {
|
|
return fmt.Errorf("unable to cast %v", i.value)
|
|
}
|
|
(*i.dict)[key] = tmp
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// String returns a readable representation of this value (for usage defaults)
|
|
func (i *MapBase[T, C, VC]) String() string {
|
|
v := i.Value()
|
|
var t T
|
|
if reflect.TypeOf(t).Kind() == reflect.String {
|
|
return fmt.Sprintf("%v", v)
|
|
}
|
|
return fmt.Sprintf("%T{%s}", v, i.ToString(v))
|
|
}
|
|
|
|
// Serialize allows MapBase to fulfill Serializer
|
|
func (i *MapBase[T, C, VC]) Serialize() string {
|
|
jsonBytes, _ := json.Marshal(i.dict)
|
|
return fmt.Sprintf("%s%s", slPfx, string(jsonBytes))
|
|
}
|
|
|
|
// Value returns the mapping of values set by this flag
|
|
func (i *MapBase[T, C, VC]) Value() map[string]T {
|
|
if i.dict == nil {
|
|
return map[string]T{}
|
|
}
|
|
return *i.dict
|
|
}
|
|
|
|
// Get returns the mapping of values set by this flag
|
|
func (i *MapBase[T, C, VC]) Get() interface{} {
|
|
return *i.dict
|
|
}
|
|
|
|
func (i MapBase[T, C, VC]) ToString(t map[string]T) string {
|
|
var defaultVals []string
|
|
var vc VC
|
|
for _, k := range sortedKeys(t) {
|
|
defaultVals = append(defaultVals, k+defaultMapFlagKeyValueSeparator+vc.ToString(t[k]))
|
|
}
|
|
return strings.Join(defaultVals, ", ")
|
|
}
|
|
|
|
func sortedKeys[T any](dict map[string]T) []string {
|
|
keys := make([]string, 0, len(dict))
|
|
for k := range dict {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Strings(keys)
|
|
return keys
|
|
}
|