func/vendor/github.com/iancoleman/orderedmap/orderedmap.go

330 lines
8.1 KiB
Go

package orderedmap
import (
"encoding/json"
"errors"
"sort"
"strings"
)
var NoValueError = errors.New("No value for this key")
type KeyIndex struct {
Key string
Index int
}
type ByIndex []KeyIndex
func (a ByIndex) Len() int { return len(a) }
func (a ByIndex) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByIndex) Less(i, j int) bool { return a[i].Index < a[j].Index }
type Pair struct {
key string
value interface{}
}
func (kv *Pair) Key() string {
return kv.key
}
func (kv *Pair) Value() interface{} {
return kv.value
}
type ByPair struct {
Pairs []*Pair
LessFunc func(a *Pair, j *Pair) bool
}
func (a ByPair) Len() int { return len(a.Pairs) }
func (a ByPair) Swap(i, j int) { a.Pairs[i], a.Pairs[j] = a.Pairs[j], a.Pairs[i] }
func (a ByPair) Less(i, j int) bool { return a.LessFunc(a.Pairs[i], a.Pairs[j]) }
type OrderedMap struct {
keys []string
values map[string]interface{}
}
func New() *OrderedMap {
o := OrderedMap{}
o.keys = []string{}
o.values = map[string]interface{}{}
return &o
}
func (o *OrderedMap) Get(key string) (interface{}, bool) {
val, exists := o.values[key]
return val, exists
}
func (o *OrderedMap) Set(key string, value interface{}) {
_, exists := o.values[key]
if !exists {
o.keys = append(o.keys, key)
}
o.values[key] = value
}
func (o *OrderedMap) Delete(key string) {
// check key is in use
_, ok := o.values[key]
if !ok {
return
}
// remove from keys
for i, k := range o.keys {
if k == key {
o.keys = append(o.keys[:i], o.keys[i+1:]...)
break
}
}
// remove from values
delete(o.values, key)
}
func (o *OrderedMap) Keys() []string {
return o.keys
}
// SortKeys Sort the map keys using your sort func
func (o *OrderedMap) SortKeys(sortFunc func(keys []string)) {
sortFunc(o.keys)
}
// Sort Sort the map using your sort func
func (o *OrderedMap) Sort(lessFunc func(a *Pair, b *Pair) bool) {
pairs := make([]*Pair, len(o.keys))
for i, key := range o.keys {
pairs[i] = &Pair{key, o.values[key]}
}
sort.Sort(ByPair{pairs, lessFunc})
for i, pair := range pairs {
o.keys[i] = pair.key
}
}
func (o *OrderedMap) UnmarshalJSON(b []byte) error {
if o.values == nil {
o.values = map[string]interface{}{}
}
var err error
err = mapStringToOrderedMap(string(b), o)
if err != nil {
return err
}
return nil
}
func mapStringToOrderedMap(s string, o *OrderedMap) error {
// parse string into map
m := map[string]interface{}{}
err := json.Unmarshal([]byte(s), &m)
if err != nil {
return err
}
// Get the order of the keys
orderedKeys := []KeyIndex{}
for k, _ := range m {
kEscaped := strings.Replace(k, `"`, `\"`, -1)
kQuoted := `"` + kEscaped + `"`
// Find how much content exists before this key.
// If all content from this key and after is replaced with a close
// brace, it should still form a valid json string.
sTrimmed := s
for len(sTrimmed) > 0 {
lastIndex := strings.LastIndex(sTrimmed, kQuoted)
if lastIndex == -1 {
break
}
sTrimmed = sTrimmed[0:lastIndex]
sTrimmed = strings.TrimSpace(sTrimmed)
if len(sTrimmed) > 0 && sTrimmed[len(sTrimmed)-1] == ',' {
sTrimmed = sTrimmed[0 : len(sTrimmed)-1]
}
maybeValidJson := sTrimmed + "}"
testMap := map[string]interface{}{}
err := json.Unmarshal([]byte(maybeValidJson), &testMap)
if err == nil {
// record the position of this key in s
ki := KeyIndex{
Key: k,
Index: len(sTrimmed),
}
orderedKeys = append(orderedKeys, ki)
// shorten the string to get the next key
startOfValueIndex := lastIndex + len(kQuoted)
valueStr := s[startOfValueIndex : len(s)-1]
valueStr = strings.TrimSpace(valueStr)
if len(valueStr) > 0 && valueStr[0] == ':' {
valueStr = valueStr[1:len(valueStr)]
}
valueStr = strings.TrimSpace(valueStr)
if valueStr[0] == '{' {
// if the value for this key is a map
// find end of valueStr by removing everything after last }
// until it forms valid json
hasValidJson := false
i := 1
for i < len(valueStr) && !hasValidJson {
if valueStr[i] != '}' {
i = i + 1
continue
}
subTestMap := map[string]interface{}{}
testValue := valueStr[0 : i+1]
err = json.Unmarshal([]byte(testValue), &subTestMap)
if err == nil {
hasValidJson = true
valueStr = testValue
break
}
i = i + 1
}
// convert to orderedmap
// this may be recursive it values in the map are also maps
if hasValidJson {
newMap := New()
err := mapStringToOrderedMap(valueStr, newMap)
if err != nil {
return err
}
m[k] = *newMap
}
} else if valueStr[0] == '[' {
// if the value for this key is a slice
// find end of valueStr by removing everything after last ]
// until it forms valid json
hasValidJson := false
i := 1
for i < len(valueStr) && !hasValidJson {
if valueStr[i] != ']' {
i = i + 1
continue
}
subTestSlice := []interface{}{}
testValue := valueStr[0 : i+1]
err = json.Unmarshal([]byte(testValue), &subTestSlice)
if err == nil {
hasValidJson = true
valueStr = testValue
break
}
i = i + 1
}
// convert to slice with any map items converted to
// orderedmaps
// this may be recursive if values in the slice are slices
if hasValidJson {
newSlice := []interface{}{}
err := sliceStringToSliceWithOrderedMaps(valueStr, &newSlice)
if err != nil {
return err
}
m[k] = newSlice
}
} else {
o.Set(k, m[k])
}
break
}
}
}
// Sort the keys
sort.Sort(ByIndex(orderedKeys))
// Convert sorted keys to string slice
k := []string{}
for _, ki := range orderedKeys {
k = append(k, ki.Key)
}
// Set the OrderedMap values
o.values = m
o.keys = k
return nil
}
func sliceStringToSliceWithOrderedMaps(valueStr string, newSlice *[]interface{}) error {
// if the value for this key is a []interface, convert any map items to an orderedmap.
// find end of valueStr by removing everything after last ]
// until it forms valid json
itemsStr := strings.TrimSpace(valueStr)
itemsStr = itemsStr[1 : len(itemsStr)-1]
// get next item in the slice
itemIndex := 0
startItem := 0
endItem := 0
for endItem <= len(itemsStr) {
couldBeItemEnd := false
couldBeItemEnd = couldBeItemEnd || endItem == len(itemsStr)
couldBeItemEnd = couldBeItemEnd || (endItem < len(itemsStr) && itemsStr[endItem] == ',')
if !couldBeItemEnd {
endItem = endItem + 1
continue
}
// if this substring compiles to json, it's the next item
possibleItemStr := strings.TrimSpace(itemsStr[startItem:endItem])
var possibleItem interface{}
err := json.Unmarshal([]byte(possibleItemStr), &possibleItem)
if err != nil {
endItem = endItem + 1
continue
}
if possibleItemStr[0] == '{' {
// if item is map, convert to orderedmap
oo := New()
err := mapStringToOrderedMap(possibleItemStr, oo)
if err != nil {
return err
}
// add new orderedmap item to new slice
slice := *newSlice
slice = append(slice, *oo)
*newSlice = slice
} else if possibleItemStr[0] == '[' {
// if item is slice, convert to slice with orderedmaps
newItem := []interface{}{}
err := sliceStringToSliceWithOrderedMaps(possibleItemStr, &newItem)
if err != nil {
return err
}
// replace original slice item with new slice
slice := *newSlice
slice = append(slice, newItem)
*newSlice = slice
} else {
// any non-slice and non-map item, just add json parsed item
slice := *newSlice
slice = append(slice, possibleItem)
*newSlice = slice
}
// remove this item from itemsStr
startItem = endItem + 1
endItem = endItem + 1
itemIndex = itemIndex + 1
}
return nil
}
func (o OrderedMap) MarshalJSON() ([]byte, error) {
s := "{"
for _, k := range o.keys {
// add key
kEscaped := strings.Replace(k, `"`, `\"`, -1)
s = s + `"` + kEscaped + `":`
// add value
v := o.values[k]
vBytes, err := json.Marshal(v)
if err != nil {
return []byte{}, err
}
s = s + string(vBytes) + ","
}
if len(o.keys) > 0 {
s = s[0 : len(s)-1]
}
s = s + "}"
return []byte(s), nil
}