package utils import ( "encoding/json" "fmt" "strconv" "gopkg.in/yaml.v2" ) type InsertionOrderedStringMap struct { keys []string `yaml:"-"` values map[string]interface{} } func NewEmptyInsertionOrderedStringMap(size int) *InsertionOrderedStringMap { return &InsertionOrderedStringMap{ keys: make([]string, 0, size), values: make(map[string]interface{}, size), } } func NewInsertionOrderedStringMap(stringMap map[string]interface{}) *InsertionOrderedStringMap { result := NewEmptyInsertionOrderedStringMap(len(stringMap)) for k, v := range stringMap { result.Set(k, v) } return result } func (insertionOrderedStringMap *InsertionOrderedStringMap) Len() int { return len(insertionOrderedStringMap.values) } func (insertionOrderedStringMap *InsertionOrderedStringMap) UnmarshalYAML(unmarshal func(interface{}) error) error { var data yaml.MapSlice if err := unmarshal(&data); err != nil { return err } insertionOrderedStringMap.values = make(map[string]interface{}) for _, v := range data { insertionOrderedStringMap.Set(v.Key.(string), toString(v.Value)) } return nil } func (insertionOrderedStringMap *InsertionOrderedStringMap) UnmarshalJSON(data []byte) error { var dataMap map[string]interface{} if err := json.Unmarshal(data, &dataMap); err != nil { return err } insertionOrderedStringMap.values = make(map[string]interface{}) for k, v := range dataMap { insertionOrderedStringMap.Set(k, toString(v)) } return nil } // toString converts an interface to string in a quick way func toString(data interface{}) interface{} { switch s := data.(type) { case nil: return "" case string: return s case bool: return strconv.FormatBool(s) case float64: return strconv.FormatFloat(s, 'f', -1, 64) case float32: return strconv.FormatFloat(float64(s), 'f', -1, 32) case int: return strconv.Itoa(s) case int64: return strconv.FormatInt(s, 10) case int32: return strconv.Itoa(int(s)) case int16: return strconv.FormatInt(int64(s), 10) case int8: return strconv.FormatInt(int64(s), 10) case uint: return strconv.FormatUint(uint64(s), 10) case uint64: return strconv.FormatUint(s, 10) case uint32: return strconv.FormatUint(uint64(s), 10) case uint16: return strconv.FormatUint(uint64(s), 10) case uint8: return strconv.FormatUint(uint64(s), 10) case []byte: return string(s) case []interface{}: return data default: return fmt.Sprintf("%v", data) } } func (insertionOrderedStringMap *InsertionOrderedStringMap) ForEach(fn func(key string, data interface{})) { for _, key := range insertionOrderedStringMap.keys { fn(key, insertionOrderedStringMap.values[key]) } } func (insertionOrderedStringMap *InsertionOrderedStringMap) Set(key string, value interface{}) { _, present := insertionOrderedStringMap.values[key] insertionOrderedStringMap.values[key] = value if !present { insertionOrderedStringMap.keys = append(insertionOrderedStringMap.keys, key) } }