| package flatmap |
| |
| import ( |
| "fmt" |
| "reflect" |
| ) |
| |
| // Flatten takes a structure and turns into a flat map[string]string. |
| // |
| // Within the "thing" parameter, only primitive values are allowed. Structs are |
| // not supported. Therefore, it can only be slices, maps, primitives, and |
| // any combination of those together. |
| // |
| // See the tests for examples of what inputs are turned into. |
| func Flatten(thing map[string]interface{}) Map { |
| result := make(map[string]string) |
| |
| for k, raw := range thing { |
| flatten(result, k, reflect.ValueOf(raw)) |
| } |
| |
| return Map(result) |
| } |
| |
| func flatten(result map[string]string, prefix string, v reflect.Value) { |
| if v.Kind() == reflect.Interface { |
| v = v.Elem() |
| } |
| |
| switch v.Kind() { |
| case reflect.Bool: |
| if v.Bool() { |
| result[prefix] = "true" |
| } else { |
| result[prefix] = "false" |
| } |
| case reflect.Int: |
| result[prefix] = fmt.Sprintf("%d", v.Int()) |
| case reflect.Map: |
| flattenMap(result, prefix, v) |
| case reflect.Slice: |
| flattenSlice(result, prefix, v) |
| case reflect.String: |
| result[prefix] = v.String() |
| default: |
| panic(fmt.Sprintf("Unknown: %s", v)) |
| } |
| } |
| |
| func flattenMap(result map[string]string, prefix string, v reflect.Value) { |
| for _, k := range v.MapKeys() { |
| if k.Kind() == reflect.Interface { |
| k = k.Elem() |
| } |
| |
| if k.Kind() != reflect.String { |
| panic(fmt.Sprintf("%s: map key is not string: %s", prefix, k)) |
| } |
| |
| flatten(result, fmt.Sprintf("%s.%s", prefix, k.String()), v.MapIndex(k)) |
| } |
| } |
| |
| func flattenSlice(result map[string]string, prefix string, v reflect.Value) { |
| prefix = prefix + "." |
| |
| result[prefix+"#"] = fmt.Sprintf("%d", v.Len()) |
| for i := 0; i < v.Len(); i++ { |
| flatten(result, fmt.Sprintf("%s%d", prefix, i), v.Index(i)) |
| } |
| } |