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