74 lines
1.4 KiB
Go
74 lines
1.4 KiB
Go
package stats
|
|
|
|
import (
|
|
"reflect"
|
|
"strings"
|
|
)
|
|
|
|
type FlatMap map[string]interface{}
|
|
|
|
func isEmptyValue(v reflect.Value) bool {
|
|
return v.Interface() == reflect.Zero(v.Type()).Interface()
|
|
}
|
|
|
|
func keyForField(field reflect.StructField, v reflect.Value) string {
|
|
if tag := field.Tag.Get("json"); tag != "" {
|
|
tokens := strings.SplitN(tag, ",", 2)
|
|
name := tokens[0]
|
|
opts := ""
|
|
|
|
if len(tokens) > 1 {
|
|
opts = tokens[1]
|
|
}
|
|
|
|
if name == "-" || strings.Contains(opts, "omitempty") && isEmptyValue(v) {
|
|
return ""
|
|
} else if name != "" {
|
|
return name
|
|
}
|
|
}
|
|
|
|
return field.Name
|
|
}
|
|
|
|
func recursiveFlatten(val reflect.Value, prefix string, output FlatMap) int {
|
|
valType := val.Type()
|
|
added := 0
|
|
|
|
for i := 0; i < val.NumField(); i++ {
|
|
child := val.Field(i)
|
|
childType := valType.Field(i)
|
|
key := prefix + keyForField(childType, child)
|
|
|
|
if childType.PkgPath != "" || key == "" {
|
|
continue
|
|
} else if child.Kind() == reflect.Struct {
|
|
if recursiveFlatten(child, key+".", output) != 0 {
|
|
continue
|
|
}
|
|
}
|
|
|
|
output[key] = child.Addr().Interface()
|
|
added++
|
|
}
|
|
|
|
return added
|
|
}
|
|
|
|
func flattenPointer(val reflect.Value) FlatMap {
|
|
if val.Kind() == reflect.Ptr {
|
|
return flattenPointer(val.Elem())
|
|
}
|
|
|
|
if val.Kind() != reflect.Struct {
|
|
panic("must be called with a struct type")
|
|
}
|
|
|
|
m := FlatMap{}
|
|
recursiveFlatten(val, "", m)
|
|
return m
|
|
}
|
|
|
|
func Flatten(val interface{}) FlatMap {
|
|
return flattenPointer(reflect.ValueOf(val))
|
|
}
|