github.com/diptanu/nomad@v0.5.7-0.20170516172507-d72e86cbe3d9/helper/flatmap/flatmap.go (about) 1 package flatmap 2 3 import ( 4 "fmt" 5 "reflect" 6 ) 7 8 // Flatten takes an object and returns a flat map of the object. The keys of the 9 // map is the path of the field names until a primitive field is reached and the 10 // value is a string representation of the terminal field. 11 func Flatten(obj interface{}, filter []string, primitiveOnly bool) map[string]string { 12 flat := make(map[string]string) 13 v := reflect.ValueOf(obj) 14 if !v.IsValid() { 15 return nil 16 } 17 18 flatten("", v, primitiveOnly, false, flat) 19 for _, f := range filter { 20 if _, ok := flat[f]; ok { 21 delete(flat, f) 22 } 23 } 24 return flat 25 } 26 27 // flatten recursively calls itself to create a flatmap representation of the 28 // passed value. The results are stored into the output map and the keys are 29 // the fields prepended with the passed prefix. 30 // XXX: A current restriction is that maps only support string keys. 31 func flatten(prefix string, v reflect.Value, primitiveOnly, enteredStruct bool, output map[string]string) { 32 switch v.Kind() { 33 case reflect.Bool: 34 output[prefix] = fmt.Sprintf("%v", v.Bool()) 35 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 36 output[prefix] = fmt.Sprintf("%v", v.Int()) 37 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 38 output[prefix] = fmt.Sprintf("%v", v.Uint()) 39 case reflect.Float32, reflect.Float64: 40 output[prefix] = fmt.Sprintf("%v", v.Float()) 41 case reflect.Complex64, reflect.Complex128: 42 output[prefix] = fmt.Sprintf("%v", v.Complex()) 43 case reflect.String: 44 output[prefix] = fmt.Sprintf("%v", v.String()) 45 case reflect.Invalid: 46 output[prefix] = "nil" 47 case reflect.Ptr: 48 if primitiveOnly && enteredStruct { 49 return 50 } 51 52 e := v.Elem() 53 if !e.IsValid() { 54 output[prefix] = "nil" 55 } 56 flatten(prefix, e, primitiveOnly, enteredStruct, output) 57 case reflect.Map: 58 for _, k := range v.MapKeys() { 59 if k.Kind() == reflect.Interface { 60 k = k.Elem() 61 } 62 63 if k.Kind() != reflect.String { 64 panic(fmt.Sprintf("%q: map key is not string: %s", prefix, k)) 65 } 66 67 flatten(getSubKeyPrefix(prefix, k.String()), v.MapIndex(k), primitiveOnly, enteredStruct, output) 68 } 69 case reflect.Struct: 70 if primitiveOnly && enteredStruct { 71 return 72 } 73 enteredStruct = true 74 75 t := v.Type() 76 for i := 0; i < v.NumField(); i++ { 77 name := t.Field(i).Name 78 val := v.Field(i) 79 if val.Kind() == reflect.Interface && !val.IsNil() { 80 val = val.Elem() 81 } 82 83 flatten(getSubPrefix(prefix, name), val, primitiveOnly, enteredStruct, output) 84 } 85 case reflect.Interface: 86 if primitiveOnly { 87 return 88 } 89 90 e := v.Elem() 91 if !e.IsValid() { 92 output[prefix] = "nil" 93 return 94 } 95 flatten(prefix, e, primitiveOnly, enteredStruct, output) 96 case reflect.Array, reflect.Slice: 97 if primitiveOnly { 98 return 99 } 100 101 if v.Kind() == reflect.Slice && v.IsNil() { 102 output[prefix] = "nil" 103 return 104 } 105 for i := 0; i < v.Len(); i++ { 106 flatten(fmt.Sprintf("%s[%d]", prefix, i), v.Index(i), primitiveOnly, enteredStruct, output) 107 } 108 default: 109 panic(fmt.Sprintf("prefix %q; unsupported type %v", prefix, v.Kind())) 110 } 111 } 112 113 // getSubPrefix takes the current prefix and the next subfield and returns an 114 // appropriate prefix. 115 func getSubPrefix(curPrefix, subField string) string { 116 newPrefix := "" 117 if curPrefix != "" { 118 newPrefix = fmt.Sprintf("%s.%s", curPrefix, subField) 119 } else { 120 newPrefix = fmt.Sprintf("%s", subField) 121 } 122 return newPrefix 123 } 124 125 // getSubKeyPrefix takes the current prefix and the next subfield and returns an 126 // appropriate prefix for a map field. 127 func getSubKeyPrefix(curPrefix, subField string) string { 128 newPrefix := "" 129 if curPrefix != "" { 130 newPrefix = fmt.Sprintf("%s[%s]", curPrefix, subField) 131 } else { 132 newPrefix = fmt.Sprintf("%s", subField) 133 } 134 return newPrefix 135 }