tracker/stats/struct_flattener.go
2014-07-24 20:28:07 -04:00

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))
}