mirror of https://github.com/knative/func.git
330 lines
8.1 KiB
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
|
|
}
|