1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
|
package internal
import (
"encoding/json"
"fmt"
"strconv"
)
// TraverseMap recursively walks the given map, calling set for each value. If the
// value is a slice, set is called for each element of the slice. The keys of
// nested maps are joined with the given delimiter.
func TraverseMap(m map[string]any, delimiter string, set func(name, value string) error) error {
return traverseMap("", m, delimiter, set)
}
func traverseMap(key string, val any, delimiter string, set func(name, value string) error) error {
switch v := val.(type) {
case string:
return set(key, v)
case json.Number:
return set(key, v.String())
case uint64:
return set(key, strconv.FormatUint(v, 10))
case int:
return set(key, strconv.Itoa(v))
case int64:
return set(key, strconv.FormatInt(v, 10))
case float64:
return set(key, strconv.FormatFloat(v, 'g', -1, 64))
case bool:
return set(key, strconv.FormatBool(v))
case nil:
return set(key, "")
case []any:
for _, v := range v {
if err := traverseMap(key, v, delimiter, set); err != nil {
return err
}
}
case map[string]any:
for k, v := range v {
if key != "" {
k = key + delimiter + k
}
if err := traverseMap(k, v, delimiter, set); err != nil {
return err
}
}
case map[any]any:
for k, v := range v {
ks := fmt.Sprint(k)
if key != "" {
ks = key + delimiter + ks
}
if err := traverseMap(ks, v, delimiter, set); err != nil {
return err
}
}
default:
return fmt.Errorf("couldn't convert %q (type %T) to string", val, val)
}
return nil
}
|