Upstream knadh/koanf state of the touched files before e3921078a3
This commit is contained in:
1 parent
219bbcd219
commit
15e76cb3d5
2 files changed
+20
-196
No files matched your search
@@ -368,26 +368,29 @@ func (ko *Koanf) Get(path string) any {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slices returns a list of Koanf instances constructed from the value
|
// Slices returns a list of Koanf instances constructed out of a
|
||||||
// at path. It accepts []map[string]any or []any containing map[string]any
|
// []map[string]any interface at the given path.
|
||||||
// elements. Non-slice elements are skipped.
|
|
||||||
func (ko *Koanf) Slices(path string) []*Koanf {
|
func (ko *Koanf) Slices(path string) []*Koanf {
|
||||||
out := []*Koanf{}
|
out := []*Koanf{}
|
||||||
if path == "" {
|
if path == "" {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
switch v := ko.Get(path).(type) {
|
// Does the path exist?
|
||||||
case []map[string]any:
|
sl, ok := ko.Get(path).([]any)
|
||||||
for _, mp := range v {
|
if !ok {
|
||||||
out = ko.appendMap(mp, out)
|
return out
|
||||||
}
|
|
||||||
case []any:
|
|
||||||
for _, item := range v {
|
|
||||||
if mp, ok := item.(map[string]any); ok {
|
|
||||||
out = ko.appendMap(mp, out)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, s := range sl {
|
||||||
|
mp, ok := s.(map[string]any)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
k := New(ko.conf.Delim)
|
||||||
|
_ = k.merge(mp, new(options))
|
||||||
|
out = append(out, k)
|
||||||
}
|
}
|
||||||
|
|
||||||
return out
|
return out
|
||||||
@@ -413,16 +416,13 @@ func (ko *Koanf) MapKeys(path string) []string {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// A value set with Set() or by a Provider such as structs keeps its native
|
mp, ok := o.(map[string]any)
|
||||||
// map type, so the keys have to be read through reflection too.
|
if !ok {
|
||||||
rv := reflect.ValueOf(o)
|
|
||||||
if rv.Kind() != reflect.Map || rv.Type().Key().Kind() != reflect.String {
|
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
out = make([]string, 0, len(mp))
|
||||||
out = make([]string, 0, rv.Len())
|
for k := range mp {
|
||||||
for _, k := range rv.MapKeys() {
|
out = append(out, k)
|
||||||
out = append(out, k.String())
|
|
||||||
}
|
}
|
||||||
sort.Strings(out)
|
sort.Strings(out)
|
||||||
return out
|
return out
|
||||||
@@ -656,10 +656,3 @@ func textUnmarshalerHookFunc() mapstructure.DecodeHookFuncType {
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// appendMap creates new Koanf instances from a map returns a slice of Koanf instances.
|
|
||||||
func (ko *Koanf) appendMap(mp map[string]any, out []*Koanf) []*Koanf {
|
|
||||||
k := New(ko.conf.Delim)
|
|
||||||
_ = k.merge(mp, new(options))
|
|
||||||
return append(out, k)
|
|
||||||
}
|
|
||||||
@@ -1307,90 +1307,6 @@ func TestSlices(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSlicesNativeMapSlice(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
// A natively-typed []map[string]any (as produced by the confmap and
|
|
||||||
// structs providers, and named in Slices' own doc) must be handled too,
|
|
||||||
// not just a []any of maps decoded by a parser. It was silently dropped.
|
|
||||||
k := koanf.New(delim)
|
|
||||||
require.NoError(t, k.Load(confmap.Provider(map[string]any{
|
|
||||||
"servers": []map[string]any{
|
|
||||||
{"host": "a", "port": 1},
|
|
||||||
{"host": "b", "port": 2},
|
|
||||||
},
|
|
||||||
}, "."), nil))
|
|
||||||
|
|
||||||
slices := k.Slices("servers")
|
|
||||||
require.Len(t, slices, 2, "Slices dropped a []map[string]any value")
|
|
||||||
assert.Equal("a", slices[0].String("host"))
|
|
||||||
assert.Equal(1, slices[0].Int("port"))
|
|
||||||
assert.Equal("b", slices[1].String("host"))
|
|
||||||
assert.Equal(2, slices[1].Int("port"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSlicesEdgeCases(t *testing.T) {
|
|
||||||
for _, tt := range []struct {
|
|
||||||
name string
|
|
||||||
value any
|
|
||||||
want []map[string]any
|
|
||||||
}{
|
|
||||||
{"nil_native_slice", []map[string]any(nil), []map[string]any{}},
|
|
||||||
{"empty_native_slice", []map[string]any{}, []map[string]any{}},
|
|
||||||
{"nil_interface_slice", []any(nil), []map[string]any{}},
|
|
||||||
{"empty_interface_slice", []any{}, []map[string]any{}},
|
|
||||||
{"nil_and_empty_native_maps", []map[string]any{nil, {}}, []map[string]any{{}, {}}},
|
|
||||||
{"nil_and_empty_interface_maps", []any{map[string]any(nil), map[string]any{}}, []map[string]any{{}, {}}},
|
|
||||||
{
|
|
||||||
"mixed_interface_slice",
|
|
||||||
[]any{nil, 42, map[string]any{"host": "a"}, "invalid", map[string]any{"host": "b"}},
|
|
||||||
[]map[string]any{{"host": "a"}, {"host": "b"}},
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
k := koanf.New(delim)
|
|
||||||
require.NoError(t, k.Load(confmap.Provider(map[string]any{"servers": tt.value}, ""), nil))
|
|
||||||
|
|
||||||
slices := k.Slices("servers")
|
|
||||||
require.NotNil(t, slices)
|
|
||||||
require.Len(t, slices, len(tt.want))
|
|
||||||
for i, child := range slices {
|
|
||||||
assert.Equal(t, tt.want[i], child.Raw())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSlicesCopyIsolation(t *testing.T) {
|
|
||||||
for _, native := range []bool{false, true} {
|
|
||||||
name := "interface_slice"
|
|
||||||
if native {
|
|
||||||
name = "native_slice"
|
|
||||||
}
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
// Reuse the same map to check that siblings are independent too.
|
|
||||||
mp := map[string]any{"nested": map[string]any{"host": "original"}}
|
|
||||||
var value any = []any{mp, mp}
|
|
||||||
if native {
|
|
||||||
value = []map[string]any{mp, mp}
|
|
||||||
}
|
|
||||||
|
|
||||||
k := koanf.New("/")
|
|
||||||
require.NoError(t, k.Load(confmap.Provider(map[string]any{"servers": value}, ""), nil))
|
|
||||||
before := k.Get("servers")
|
|
||||||
slices := k.Slices("servers")
|
|
||||||
require.Len(t, slices, 2)
|
|
||||||
assert.Equal(t, "/", slices[0].Delim())
|
|
||||||
assert.Equal(t, "original", slices[0].String("nested/host"))
|
|
||||||
|
|
||||||
require.NoError(t, slices[0].Set("nested/host", "changed"))
|
|
||||||
assert.Equal(t, "changed", slices[0].String("nested/host"))
|
|
||||||
assert.Equal(t, "original", slices[1].String("nested/host"))
|
|
||||||
assert.Equal(t, before, k.Get("servers"))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetTypes(t *testing.T) {
|
func TestGetTypes(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
@@ -1606,38 +1522,6 @@ func TestGetStringsMap(t *testing.T) {
|
|||||||
assert.Equal(map[string][]string{"k2": {"value"}}, k.StringsMap("ifaces3"), "types don't match")
|
assert.Equal(map[string][]string{"k2": {"value"}}, k.StringsMap("ifaces3"), "types don't match")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetStringsMapEmptySlices(t *testing.T) {
|
|
||||||
for _, tt := range []struct {
|
|
||||||
name string
|
|
||||||
value any
|
|
||||||
}{
|
|
||||||
{"string_slices", map[string][]string{"empty": {}, "nil": nil}},
|
|
||||||
{"interface_slices", map[string][]any{"empty": {}, "nil": nil}},
|
|
||||||
{"interface_map", map[string]any{"empty": []any{}, "nil": []any(nil)}},
|
|
||||||
} {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
k := koanf.New(delim)
|
|
||||||
require.NoError(t, k.Set("lists", tt.value))
|
|
||||||
want := map[string][]string{"empty": nil, "nil": nil}
|
|
||||||
require.Equal(t, want, k.StringsMap("lists"))
|
|
||||||
require.Equal(t, want, k.MustStringsMap("lists"))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("json", func(t *testing.T) {
|
|
||||||
k := koanf.New(delim)
|
|
||||||
require.NoError(t, k.Load(rawbytes.Provider([]byte(`{"lists":{"empty":[],"populated":["value"]}}`)), json.Parser()))
|
|
||||||
require.Equal(t, map[string][]string{"empty": nil, "populated": {"value"}}, k.StringsMap("lists"))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("invalid_element", func(t *testing.T) {
|
|
||||||
k := koanf.New(delim)
|
|
||||||
require.NoError(t, k.Set("lists", map[string][]any{"empty": {}, "invalid": {42}}))
|
|
||||||
require.Empty(t, k.StringsMap("lists"))
|
|
||||||
require.Panics(t, func() { k.MustStringsMap("lists") })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBoolsNativeSlice(t *testing.T) {
|
func TestBoolsNativeSlice(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
@@ -1649,59 +1533,6 @@ func TestBoolsNativeSlice(t *testing.T) {
|
|||||||
assert.Equal([]bool{true, false, true}, k.Bools("bools"))
|
assert.Equal([]bool{true, false, true}, k.Bools("bools"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTypedGettersNativeMaps(t *testing.T) {
|
|
||||||
for _, tt := range []struct {
|
|
||||||
name string
|
|
||||||
value any
|
|
||||||
get func(*koanf.Koanf, string) any
|
|
||||||
}{
|
|
||||||
{"int", map[string]int{"a": 1, "b": 2}, func(k *koanf.Koanf, path string) any { return k.IntMap(path) }},
|
|
||||||
{"int64", map[string]int64{"a": 1 << 54, "b": -2}, func(k *koanf.Koanf, path string) any { return k.Int64Map(path) }},
|
|
||||||
{"float64", map[string]float64{"a": 1.5, "b": -2.5}, func(k *koanf.Koanf, path string) any { return k.Float64Map(path) }},
|
|
||||||
{"bool", map[string]bool{"a": true, "b": false}, func(k *koanf.Koanf, path string) any { return k.BoolMap(path) }},
|
|
||||||
{"nil_int", map[string]int(nil), func(k *koanf.Koanf, path string) any { return k.IntMap(path) }},
|
|
||||||
{"nil_int64", map[string]int64(nil), func(k *koanf.Koanf, path string) any { return k.Int64Map(path) }},
|
|
||||||
{"nil_float64", map[string]float64(nil), func(k *koanf.Koanf, path string) any { return k.Float64Map(path) }},
|
|
||||||
{"nil_bool", map[string]bool(nil), func(k *koanf.Koanf, path string) any { return k.BoolMap(path) }},
|
|
||||||
} {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
k := koanf.New(delim)
|
|
||||||
require.NoError(t, k.Load(confmap.Provider(map[string]any{"value": tt.value}, ""), nil))
|
|
||||||
got := tt.get(k, "value")
|
|
||||||
require.NotNil(t, got)
|
|
||||||
if strings.HasPrefix(tt.name, "nil_") {
|
|
||||||
assert.Empty(t, got)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
assert.Equal(t, tt.value, got)
|
|
||||||
|
|
||||||
// Changing the returned map must not change the stored configuration.
|
|
||||||
switch mp := got.(type) {
|
|
||||||
case map[string]int:
|
|
||||||
mp["a"] = 0
|
|
||||||
case map[string]int64:
|
|
||||||
mp["a"] = 0
|
|
||||||
case map[string]float64:
|
|
||||||
mp["a"] = 0
|
|
||||||
case map[string]bool:
|
|
||||||
mp["a"] = false
|
|
||||||
}
|
|
||||||
assert.Equal(t, tt.value, tt.get(k, "value"))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMapKeysNativeMap(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
k := koanf.New(delim)
|
|
||||||
assert.NoError(k.Load(confmap.Provider(map[string]any{
|
|
||||||
"ints": map[string]int64{"a": 1, "b": 2},
|
|
||||||
}, "."), nil))
|
|
||||||
|
|
||||||
assert.Equal([]string{"a", "b"}, k.MapKeys("ints"), "map keys mismatch for a natively-typed map")
|
|
||||||
}
|
|
||||||
|
|
||||||
// waitTimeout waits for the waitgroup for the specified max timeout.
|
// waitTimeout waits for the waitgroup for the specified max timeout.
|
||||||
// Returns true if waiting timed out.
|
// Returns true if waiting timed out.
|
||||||
func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {
|
func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {
|
||||||
|
|||||||
Reference in new issue
Block a user