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