forked from ebhomengo/niki
1
0
Fork 0
niki/vendor/github.com/knadh/koanf/koanf.go

872 lines
14 KiB
Go
Raw Normal View History

2024-02-18 10:42:21 +00:00
package koanf
import (
"bytes"
"fmt"
"sort"
"strconv"
"github.com/knadh/koanf/maps"
"github.com/mitchellh/copystructure"
"github.com/mitchellh/mapstructure"
)
// Koanf is the configuration apparatus.
2024-02-18 10:42:21 +00:00
type Koanf struct {
confMap map[string]interface{}
2024-02-18 10:42:21 +00:00
confMapFlat map[string]interface{}
keyMap KeyMap
conf Conf
2024-02-18 10:42:21 +00:00
}
// Conf is the Koanf configuration.
2024-02-18 10:42:21 +00:00
type Conf struct {
2024-02-18 10:42:21 +00:00
// Delim is the delimiter to use
2024-02-18 10:42:21 +00:00
// when specifying config key paths, for instance a . for `parent.child.key`
2024-02-18 10:42:21 +00:00
// or a / for `parent/child/key`.
2024-02-18 10:42:21 +00:00
Delim string
// StrictMerge makes the merging behavior strict.
2024-02-18 10:42:21 +00:00
// Meaning when loading two files that have the same key,
2024-02-18 10:42:21 +00:00
// the first loaded file will define the desired type, and if the second file loads
2024-02-18 10:42:21 +00:00
// a different type will cause an error.
2024-02-18 10:42:21 +00:00
StrictMerge bool
}
// KeyMap represents a map of flattened delimited keys and the non-delimited
2024-02-18 10:42:21 +00:00
// parts as their slices. For nested keys, the map holds all levels of path combinations.
2024-02-18 10:42:21 +00:00
// For example, the nested structure `parent -> child -> key` will produce the map:
2024-02-18 10:42:21 +00:00
// parent.child.key => [parent, child, key]
2024-02-18 10:42:21 +00:00
// parent.child => [parent, child]
2024-02-18 10:42:21 +00:00
// parent => [parent]
2024-02-18 10:42:21 +00:00
type KeyMap map[string][]string
// UnmarshalConf represents configuration options used by
2024-02-18 10:42:21 +00:00
// Unmarshal() to unmarshal conf maps into arbitrary structs.
2024-02-18 10:42:21 +00:00
type UnmarshalConf struct {
2024-02-18 10:42:21 +00:00
// Tag is the struct field tag to unmarshal.
2024-02-18 10:42:21 +00:00
// `koanf` is used if left empty.
2024-02-18 10:42:21 +00:00
Tag string
// If this is set to true, instead of unmarshalling nested structures
2024-02-18 10:42:21 +00:00
// based on the key path, keys are taken literally to unmarshal into
2024-02-18 10:42:21 +00:00
// a flat struct. For example:
2024-02-18 10:42:21 +00:00
// ```
2024-02-18 10:42:21 +00:00
// type MyStuff struct {
2024-02-18 10:42:21 +00:00
// Child1Name string `koanf:"parent1.child1.name"`
2024-02-18 10:42:21 +00:00
// Child2Name string `koanf:"parent2.child2.name"`
2024-02-18 10:42:21 +00:00
// Type string `koanf:"json"`
2024-02-18 10:42:21 +00:00
// }
2024-02-18 10:42:21 +00:00
// ```
FlatPaths bool
2024-02-18 10:42:21 +00:00
DecoderConfig *mapstructure.DecoderConfig
}
// New returns a new instance of Koanf. delim is the delimiter to use
2024-02-18 10:42:21 +00:00
// when specifying config key paths, for instance a . for `parent.child.key`
2024-02-18 10:42:21 +00:00
// or a / for `parent/child/key`.
2024-02-18 10:42:21 +00:00
func New(delim string) *Koanf {
2024-02-18 10:42:21 +00:00
return NewWithConf(Conf{
Delim: delim,
2024-02-18 10:42:21 +00:00
StrictMerge: false,
})
2024-02-18 10:42:21 +00:00
}
// NewWithConf returns a new instance of Koanf based on the Conf.
2024-02-18 10:42:21 +00:00
func NewWithConf(conf Conf) *Koanf {
2024-02-18 10:42:21 +00:00
return &Koanf{
confMap: make(map[string]interface{}),
2024-02-18 10:42:21 +00:00
confMapFlat: make(map[string]interface{}),
keyMap: make(KeyMap),
conf: conf,
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
// Load takes a Provider that either provides a parsed config map[string]interface{}
2024-02-18 10:42:21 +00:00
// in which case pa (Parser) can be nil, or raw bytes to be parsed, where a Parser
2024-02-18 10:42:21 +00:00
// can be provided to parse. Additionally, options can be passed which modify the
2024-02-18 10:42:21 +00:00
// load behavior, such as passing a custom merge function.
2024-02-18 10:42:21 +00:00
func (ko *Koanf) Load(p Provider, pa Parser, opts ...Option) error {
2024-02-18 10:42:21 +00:00
var (
mp map[string]interface{}
2024-02-18 10:42:21 +00:00
err error
)
if p == nil {
2024-02-18 10:42:21 +00:00
return fmt.Errorf("load received a nil provider")
2024-02-18 10:42:21 +00:00
}
// No Parser is given. Call the Provider's Read() method to get
2024-02-18 10:42:21 +00:00
// the config map.
2024-02-18 10:42:21 +00:00
if pa == nil {
2024-02-18 10:42:21 +00:00
mp, err = p.Read()
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
// There's a Parser. Get raw bytes from the Provider to parse.
2024-02-18 10:42:21 +00:00
b, err := p.ReadBytes()
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
mp, err = pa.Unmarshal(b)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
return ko.merge(mp, newOptions(opts))
2024-02-18 10:42:21 +00:00
}
// Keys returns the slice of all flattened keys in the loaded configuration
2024-02-18 10:42:21 +00:00
// sorted alphabetically.
2024-02-18 10:42:21 +00:00
func (ko *Koanf) Keys() []string {
2024-02-18 10:42:21 +00:00
out := make([]string, 0, len(ko.confMapFlat))
2024-02-18 10:42:21 +00:00
for k := range ko.confMapFlat {
2024-02-18 10:42:21 +00:00
out = append(out, k)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
sort.Strings(out)
2024-02-18 10:42:21 +00:00
return out
2024-02-18 10:42:21 +00:00
}
// KeyMap returns a map of flattened keys and the individual parts of the
2024-02-18 10:42:21 +00:00
// key as slices. eg: "parent.child.key" => ["parent", "child", "key"]
2024-02-18 10:42:21 +00:00
func (ko *Koanf) KeyMap() KeyMap {
2024-02-18 10:42:21 +00:00
out := make(KeyMap, len(ko.keyMap))
2024-02-18 10:42:21 +00:00
for key, parts := range ko.keyMap {
2024-02-18 10:42:21 +00:00
out[key] = make([]string, len(parts))
2024-02-18 10:42:21 +00:00
copy(out[key][:], parts[:])
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return out
2024-02-18 10:42:21 +00:00
}
// All returns a map of all flattened key paths and their values.
2024-02-18 10:42:21 +00:00
// Note that it uses maps.Copy to create a copy that uses
2024-02-18 10:42:21 +00:00
// json.Marshal which changes the numeric types to float64.
2024-02-18 10:42:21 +00:00
func (ko *Koanf) All() map[string]interface{} {
2024-02-18 10:42:21 +00:00
return maps.Copy(ko.confMapFlat)
2024-02-18 10:42:21 +00:00
}
// Raw returns a copy of the full raw conf map.
2024-02-18 10:42:21 +00:00
// Note that it uses maps.Copy to create a copy that uses
2024-02-18 10:42:21 +00:00
// json.Marshal which changes the numeric types to float64.
2024-02-18 10:42:21 +00:00
func (ko *Koanf) Raw() map[string]interface{} {
2024-02-18 10:42:21 +00:00
return maps.Copy(ko.confMap)
2024-02-18 10:42:21 +00:00
}
// Sprint returns a key -> value string representation
2024-02-18 10:42:21 +00:00
// of the config map with keys sorted alphabetically.
2024-02-18 10:42:21 +00:00
func (ko *Koanf) Sprint() string {
2024-02-18 10:42:21 +00:00
b := bytes.Buffer{}
2024-02-18 10:42:21 +00:00
for _, k := range ko.Keys() {
2024-02-18 10:42:21 +00:00
b.Write([]byte(fmt.Sprintf("%s -> %v\n", k, ko.confMapFlat[k])))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return b.String()
2024-02-18 10:42:21 +00:00
}
// Print prints a key -> value string representation
2024-02-18 10:42:21 +00:00
// of the config map with keys sorted alphabetically.
2024-02-18 10:42:21 +00:00
func (ko *Koanf) Print() {
2024-02-18 10:42:21 +00:00
fmt.Print(ko.Sprint())
2024-02-18 10:42:21 +00:00
}
// Cut cuts the config map at a given key path into a sub map and
2024-02-18 10:42:21 +00:00
// returns a new Koanf instance with the cut config map loaded.
2024-02-18 10:42:21 +00:00
// For instance, if the loaded config has a path that looks like
2024-02-18 10:42:21 +00:00
// parent.child.sub.a.b, `Cut("parent.child")` returns a new Koanf
2024-02-18 10:42:21 +00:00
// instance with the config map `sub.a.b` where everything above
2024-02-18 10:42:21 +00:00
// `parent.child` are cut out.
2024-02-18 10:42:21 +00:00
func (ko *Koanf) Cut(path string) *Koanf {
2024-02-18 10:42:21 +00:00
out := make(map[string]interface{})
// Cut only makes sense if the requested key path is a map.
2024-02-18 10:42:21 +00:00
if v, ok := ko.Get(path).(map[string]interface{}); ok {
2024-02-18 10:42:21 +00:00
out = v
2024-02-18 10:42:21 +00:00
}
n := New(ko.conf.Delim)
2024-02-18 10:42:21 +00:00
_ = n.merge(out, new(options))
2024-02-18 10:42:21 +00:00
return n
2024-02-18 10:42:21 +00:00
}
// Copy returns a copy of the Koanf instance.
2024-02-18 10:42:21 +00:00
func (ko *Koanf) Copy() *Koanf {
2024-02-18 10:42:21 +00:00
return ko.Cut("")
2024-02-18 10:42:21 +00:00
}
// Merge merges the config map of a given Koanf instance into
2024-02-18 10:42:21 +00:00
// the current instance.
2024-02-18 10:42:21 +00:00
func (ko *Koanf) Merge(in *Koanf) error {
2024-02-18 10:42:21 +00:00
return ko.merge(in.Raw(), new(options))
2024-02-18 10:42:21 +00:00
}
// MergeAt merges the config map of a given Koanf instance into
2024-02-18 10:42:21 +00:00
// the current instance as a sub map, at the given key path.
2024-02-18 10:42:21 +00:00
// If all or part of the key path is missing, it will be created.
2024-02-18 10:42:21 +00:00
// If the key path is `""`, this is equivalent to Merge.
2024-02-18 10:42:21 +00:00
func (ko *Koanf) MergeAt(in *Koanf, path string) error {
2024-02-18 10:42:21 +00:00
// No path. Merge the two config maps.
2024-02-18 10:42:21 +00:00
if path == "" {
2024-02-18 10:42:21 +00:00
return ko.Merge(in)
2024-02-18 10:42:21 +00:00
}
// Unflatten the config map with the given key path.
2024-02-18 10:42:21 +00:00
n := maps.Unflatten(map[string]interface{}{
2024-02-18 10:42:21 +00:00
path: in.Raw(),
}, ko.conf.Delim)
return ko.merge(n, new(options))
2024-02-18 10:42:21 +00:00
}
// Set sets the value at a specific key.
2024-02-18 10:42:21 +00:00
func (ko *Koanf) Set(key string, val interface{}) error {
2024-02-18 10:42:21 +00:00
// Unflatten the config map with the given key path.
2024-02-18 10:42:21 +00:00
n := maps.Unflatten(map[string]interface{}{
2024-02-18 10:42:21 +00:00
key: val,
}, ko.conf.Delim)
return ko.merge(n, new(options))
2024-02-18 10:42:21 +00:00
}
// Marshal takes a Parser implementation and marshals the config map into bytes,
2024-02-18 10:42:21 +00:00
// for example, to TOML or JSON bytes.
2024-02-18 10:42:21 +00:00
func (ko *Koanf) Marshal(p Parser) ([]byte, error) {
2024-02-18 10:42:21 +00:00
return p.Marshal(ko.Raw())
2024-02-18 10:42:21 +00:00
}
// Unmarshal unmarshals a given key path into the given struct using
2024-02-18 10:42:21 +00:00
// the mapstructure lib. If no path is specified, the whole map is unmarshalled.
2024-02-18 10:42:21 +00:00
// `koanf` is the struct field tag used to match field names. To customize,
2024-02-18 10:42:21 +00:00
// use UnmarshalWithConf(). It uses the mitchellh/mapstructure package.
2024-02-18 10:42:21 +00:00
func (ko *Koanf) Unmarshal(path string, o interface{}) error {
2024-02-18 10:42:21 +00:00
return ko.UnmarshalWithConf(path, o, UnmarshalConf{})
2024-02-18 10:42:21 +00:00
}
// UnmarshalWithConf is like Unmarshal but takes configuration params in UnmarshalConf.
2024-02-18 10:42:21 +00:00
// See mitchellh/mapstructure's DecoderConfig for advanced customization
2024-02-18 10:42:21 +00:00
// of the unmarshal behaviour.
2024-02-18 10:42:21 +00:00
func (ko *Koanf) UnmarshalWithConf(path string, o interface{}, c UnmarshalConf) error {
2024-02-18 10:42:21 +00:00
if c.DecoderConfig == nil {
2024-02-18 10:42:21 +00:00
c.DecoderConfig = &mapstructure.DecoderConfig{
2024-02-18 10:42:21 +00:00
DecodeHook: mapstructure.ComposeDecodeHookFunc(
2024-02-18 10:42:21 +00:00
mapstructure.StringToTimeDurationHookFunc(),
2024-02-18 10:42:21 +00:00
mapstructure.StringToSliceHookFunc(","),
2024-02-18 10:42:21 +00:00
mapstructure.TextUnmarshallerHookFunc()),
Metadata: nil,
Result: o,
2024-02-18 10:42:21 +00:00
WeaklyTypedInput: true,
}
2024-02-18 10:42:21 +00:00
}
if c.Tag == "" {
2024-02-18 10:42:21 +00:00
c.DecoderConfig.TagName = "koanf"
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
c.DecoderConfig.TagName = c.Tag
2024-02-18 10:42:21 +00:00
}
d, err := mapstructure.NewDecoder(c.DecoderConfig)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
// Unmarshal using flat key paths.
2024-02-18 10:42:21 +00:00
mp := ko.Get(path)
2024-02-18 10:42:21 +00:00
if c.FlatPaths {
2024-02-18 10:42:21 +00:00
if f, ok := mp.(map[string]interface{}); ok {
2024-02-18 10:42:21 +00:00
fmp, _ := maps.Flatten(f, nil, ko.conf.Delim)
2024-02-18 10:42:21 +00:00
mp = fmp
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
return d.Decode(mp)
2024-02-18 10:42:21 +00:00
}
// Delete removes all nested values from a given path.
2024-02-18 10:42:21 +00:00
// Clears all keys/values if no path is specified.
2024-02-18 10:42:21 +00:00
// Every empty, key on the path, is recursively deleted.
2024-02-18 10:42:21 +00:00
func (ko *Koanf) Delete(path string) {
2024-02-18 10:42:21 +00:00
// No path. Erase the entire map.
2024-02-18 10:42:21 +00:00
if path == "" {
2024-02-18 10:42:21 +00:00
ko.confMap = make(map[string]interface{})
2024-02-18 10:42:21 +00:00
ko.confMapFlat = make(map[string]interface{})
2024-02-18 10:42:21 +00:00
ko.keyMap = make(KeyMap)
2024-02-18 10:42:21 +00:00
return
2024-02-18 10:42:21 +00:00
}
// Does the path exist?
2024-02-18 10:42:21 +00:00
p, ok := ko.keyMap[path]
2024-02-18 10:42:21 +00:00
if !ok {
2024-02-18 10:42:21 +00:00
return
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
maps.Delete(ko.confMap, p)
// Update the flattened version as well.
2024-02-18 10:42:21 +00:00
ko.confMapFlat, ko.keyMap = maps.Flatten(ko.confMap, nil, ko.conf.Delim)
2024-02-18 10:42:21 +00:00
ko.keyMap = populateKeyParts(ko.keyMap, ko.conf.Delim)
2024-02-18 10:42:21 +00:00
}
// Get returns the raw, uncast interface{} value of a given key path
2024-02-18 10:42:21 +00:00
// in the config map. If the key path does not exist, nil is returned.
2024-02-18 10:42:21 +00:00
func (ko *Koanf) Get(path string) interface{} {
2024-02-18 10:42:21 +00:00
// No path. Return the whole conf map.
2024-02-18 10:42:21 +00:00
if path == "" {
2024-02-18 10:42:21 +00:00
return ko.Raw()
2024-02-18 10:42:21 +00:00
}
// Does the path exist?
2024-02-18 10:42:21 +00:00
p, ok := ko.keyMap[path]
2024-02-18 10:42:21 +00:00
if !ok {
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
res := maps.Search(ko.confMap, p)
// Non-reference types are okay to return directly.
2024-02-18 10:42:21 +00:00
// Other types are "copied" with maps.Copy or json.Marshal
2024-02-18 10:42:21 +00:00
// that change the numeric types to float64.
switch v := res.(type) {
2024-02-18 10:42:21 +00:00
case int, int8, int16, int32, int64, float32, float64, string, bool:
2024-02-18 10:42:21 +00:00
return v
2024-02-18 10:42:21 +00:00
case map[string]interface{}:
2024-02-18 10:42:21 +00:00
return maps.Copy(v)
2024-02-18 10:42:21 +00:00
}
out, _ := copystructure.Copy(&res)
2024-02-18 10:42:21 +00:00
if ptrOut, ok := out.(*interface{}); ok {
2024-02-18 10:42:21 +00:00
return *ptrOut
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return out
2024-02-18 10:42:21 +00:00
}
// Slices returns a list of Koanf instances constructed out of a
2024-02-18 10:42:21 +00:00
// []map[string]interface{} interface at the given path.
2024-02-18 10:42:21 +00:00
func (ko *Koanf) Slices(path string) []*Koanf {
2024-02-18 10:42:21 +00:00
out := []*Koanf{}
2024-02-18 10:42:21 +00:00
if path == "" {
2024-02-18 10:42:21 +00:00
return out
2024-02-18 10:42:21 +00:00
}
// Does the path exist?
2024-02-18 10:42:21 +00:00
sl, ok := ko.Get(path).([]interface{})
2024-02-18 10:42:21 +00:00
if !ok {
2024-02-18 10:42:21 +00:00
return out
2024-02-18 10:42:21 +00:00
}
for _, s := range sl {
2024-02-18 10:42:21 +00:00
mp, ok := s.(map[string]interface{})
2024-02-18 10:42:21 +00:00
if !ok {
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
}
k := New(ko.conf.Delim)
2024-02-18 10:42:21 +00:00
_ = k.merge(mp, new(options))
2024-02-18 10:42:21 +00:00
out = append(out, k)
2024-02-18 10:42:21 +00:00
}
return out
2024-02-18 10:42:21 +00:00
}
// Exists returns true if the given key path exists in the conf map.
2024-02-18 10:42:21 +00:00
func (ko *Koanf) Exists(path string) bool {
2024-02-18 10:42:21 +00:00
_, ok := ko.keyMap[path]
2024-02-18 10:42:21 +00:00
return ok
2024-02-18 10:42:21 +00:00
}
// MapKeys returns a sorted string list of keys in a map addressed by the
2024-02-18 10:42:21 +00:00
// given path. If the path is not a map, an empty string slice is
2024-02-18 10:42:21 +00:00
// returned.
2024-02-18 10:42:21 +00:00
func (ko *Koanf) MapKeys(path string) []string {
2024-02-18 10:42:21 +00:00
var (
out = []string{}
o = ko.Get(path)
2024-02-18 10:42:21 +00:00
)
2024-02-18 10:42:21 +00:00
if o == nil {
2024-02-18 10:42:21 +00:00
return out
2024-02-18 10:42:21 +00:00
}
mp, ok := o.(map[string]interface{})
2024-02-18 10:42:21 +00:00
if !ok {
2024-02-18 10:42:21 +00:00
return out
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
out = make([]string, 0, len(mp))
2024-02-18 10:42:21 +00:00
for k := range mp {
2024-02-18 10:42:21 +00:00
out = append(out, k)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
sort.Strings(out)
2024-02-18 10:42:21 +00:00
return out
2024-02-18 10:42:21 +00:00
}
// Delim returns delimiter in used by this instance of Koanf.
2024-02-18 10:42:21 +00:00
func (ko *Koanf) Delim() string {
2024-02-18 10:42:21 +00:00
return ko.conf.Delim
2024-02-18 10:42:21 +00:00
}
func (ko *Koanf) merge(c map[string]interface{}, opts *options) error {
2024-02-18 10:42:21 +00:00
maps.IntfaceKeysToStrings(c)
2024-02-18 10:42:21 +00:00
if opts.merge != nil {
2024-02-18 10:42:21 +00:00
if err := opts.merge(c, ko.confMap); err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
} else if ko.conf.StrictMerge {
2024-02-18 10:42:21 +00:00
if err := maps.MergeStrict(c, ko.confMap); err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
maps.Merge(c, ko.confMap)
2024-02-18 10:42:21 +00:00
}
// Maintain a flattened version as well.
2024-02-18 10:42:21 +00:00
ko.confMapFlat, ko.keyMap = maps.Flatten(ko.confMap, nil, ko.conf.Delim)
2024-02-18 10:42:21 +00:00
ko.keyMap = populateKeyParts(ko.keyMap, ko.conf.Delim)
return nil
2024-02-18 10:42:21 +00:00
}
// toInt64 takes an interface value and if it is an integer type,
2024-02-18 10:42:21 +00:00
// converts and returns int64. If it's any other type,
2024-02-18 10:42:21 +00:00
// forces it to a string and attempts to an strconv.Atoi
2024-02-18 10:42:21 +00:00
// to get an integer out.
2024-02-18 10:42:21 +00:00
func toInt64(v interface{}) (int64, error) {
2024-02-18 10:42:21 +00:00
switch i := v.(type) {
2024-02-18 10:42:21 +00:00
case int:
2024-02-18 10:42:21 +00:00
return int64(i), nil
2024-02-18 10:42:21 +00:00
case int8:
2024-02-18 10:42:21 +00:00
return int64(i), nil
2024-02-18 10:42:21 +00:00
case int16:
2024-02-18 10:42:21 +00:00
return int64(i), nil
2024-02-18 10:42:21 +00:00
case int32:
2024-02-18 10:42:21 +00:00
return int64(i), nil
2024-02-18 10:42:21 +00:00
case int64:
2024-02-18 10:42:21 +00:00
return i, nil
2024-02-18 10:42:21 +00:00
}
// Force it to a string and try to convert.
2024-02-18 10:42:21 +00:00
f, err := strconv.ParseFloat(fmt.Sprintf("%v", v), 64)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return 0, err
2024-02-18 10:42:21 +00:00
}
return int64(f), nil
2024-02-18 10:42:21 +00:00
}
// toInt64 takes a `v interface{}` value and if it is a float type,
2024-02-18 10:42:21 +00:00
// converts and returns a `float64`. If it's any other type, forces it to a
2024-02-18 10:42:21 +00:00
// string and attempts to get a float out using `strconv.ParseFloat`.
2024-02-18 10:42:21 +00:00
func toFloat64(v interface{}) (float64, error) {
2024-02-18 10:42:21 +00:00
switch i := v.(type) {
2024-02-18 10:42:21 +00:00
case float32:
2024-02-18 10:42:21 +00:00
return float64(i), nil
2024-02-18 10:42:21 +00:00
case float64:
2024-02-18 10:42:21 +00:00
return i, nil
2024-02-18 10:42:21 +00:00
}
// Force it to a string and try to convert.
2024-02-18 10:42:21 +00:00
f, err := strconv.ParseFloat(fmt.Sprintf("%v", v), 64)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return f, err
2024-02-18 10:42:21 +00:00
}
return f, nil
2024-02-18 10:42:21 +00:00
}
// toBool takes an interface value and if it is a bool type,
2024-02-18 10:42:21 +00:00
// returns it. If it's any other type, forces it to a string and attempts
2024-02-18 10:42:21 +00:00
// to parse it as a bool using strconv.ParseBool.
2024-02-18 10:42:21 +00:00
func toBool(v interface{}) (bool, error) {
2024-02-18 10:42:21 +00:00
if b, ok := v.(bool); ok {
2024-02-18 10:42:21 +00:00
return b, nil
2024-02-18 10:42:21 +00:00
}
// Force it to a string and try to convert.
2024-02-18 10:42:21 +00:00
b, err := strconv.ParseBool(fmt.Sprintf("%v", v))
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return b, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return b, nil
2024-02-18 10:42:21 +00:00
}
// populateKeyParts iterates a key map and generates all possible
2024-02-18 10:42:21 +00:00
// traversal paths. For instance, `parent.child.key` generates
2024-02-18 10:42:21 +00:00
// `parent`, and `parent.child`.
2024-02-18 10:42:21 +00:00
func populateKeyParts(m KeyMap, delim string) KeyMap {
2024-02-18 10:42:21 +00:00
out := make(KeyMap, len(m)) // The size of the result is at very least same to KeyMap
2024-02-18 10:42:21 +00:00
for _, parts := range m {
2024-02-18 10:42:21 +00:00
// parts is a slice of [parent, child, key]
2024-02-18 10:42:21 +00:00
var nk string
for i := range parts {
2024-02-18 10:42:21 +00:00
if i == 0 {
2024-02-18 10:42:21 +00:00
// On first iteration only use first part
2024-02-18 10:42:21 +00:00
nk = parts[i]
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
// If nk already contains a part (e.g. `parent`) append delim + `child`
2024-02-18 10:42:21 +00:00
nk += delim + parts[i]
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if _, ok := out[nk]; ok {
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
out[nk] = make([]string, i+1)
2024-02-18 10:42:21 +00:00
copy(out[nk][:], parts[0:i+1])
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return out
2024-02-18 10:42:21 +00:00
}