github.com/aristanetworks/goarista@v0.0.0-20240514173732-cca2755bbd44/key/stringify.go (about) 1 // Copyright (c) 2015 Arista Networks, Inc. 2 // Use of this source code is governed by the Apache License 2.0 3 // that can be found in the COPYING file. 4 5 package key 6 7 import ( 8 "encoding/base64" 9 "fmt" 10 "math" 11 "strconv" 12 "strings" 13 "unicode/utf8" 14 ) 15 16 type keyStringer interface { 17 KeyString() string 18 } 19 20 // StringKey generates a String suitable to be used as a key in a 21 // string index by calling k.StringKey, if available, otherwise it 22 // calls k.String. StringKey returns the same results as 23 // StringifyInterface(k.Key()) and should be preferred over 24 // StringifyInterface. 25 func StringKey(k Key) string { 26 if ks, ok := k.(keyStringer); ok { 27 return ks.KeyString() 28 } 29 return k.String() 30 } 31 32 // StringifyInterface transforms an arbitrary interface into a string 33 // representation suitable to be used as a key, such as in a JSON 34 // object, or as a path element. 35 // 36 // Deprecated: Use StringKey instead. 37 func StringifyInterface(key interface{}) (string, error) { 38 return stringify(key), nil 39 } 40 41 // escape checks if the string is a valid utf-8 string. 42 // If it is, it will return the string as is. 43 // If it is not, it will return the base64 representation of the byte array string 44 func escape(str string) string { 45 if utf8.ValidString(str) { 46 return str 47 } 48 return base64.StdEncoding.EncodeToString([]byte(str)) 49 } 50 51 func stringify(key interface{}) string { 52 switch key := key.(type) { 53 case nil: 54 return "<nil>" 55 case bool: 56 return strconv.FormatBool(key) 57 case uint8: 58 return strconv.FormatUint(uint64(key), 10) 59 case uint16: 60 return strconv.FormatUint(uint64(key), 10) 61 case uint32: 62 return strconv.FormatUint(uint64(key), 10) 63 case uint64: 64 return strconv.FormatUint(key, 10) 65 case int8: 66 return strconv.FormatInt(int64(key), 10) 67 case int16: 68 return strconv.FormatInt(int64(key), 10) 69 case int32: 70 return strconv.FormatInt(int64(key), 10) 71 case int64: 72 return strconv.FormatInt(key, 10) 73 case float32: 74 return "f" + strconv.FormatInt(int64(math.Float32bits(key)), 10) 75 case float64: 76 return "f" + strconv.FormatInt(int64(math.Float64bits(key)), 10) 77 case string: 78 return escape(key) 79 case map[string]interface{}: 80 keys := SortedKeys(key) 81 for i, k := range keys { 82 v := key[k] 83 keys[i] = stringify(v) 84 } 85 return strings.Join(keys, "_") 86 case *map[string]interface{}: 87 return stringify(*key) 88 case Map: 89 return key.KeyString() 90 case *Map: 91 return key.KeyString() 92 case []interface{}: 93 elements := make([]string, len(key)) 94 for i, element := range key { 95 elements[i] = stringify(element) 96 } 97 return strings.Join(elements, ",") 98 case []byte: 99 return base64.StdEncoding.EncodeToString(key) 100 case Pointer: 101 return "{" + key.Pointer().String() + "}" 102 case Path: 103 return "[" + key.String() + "]" 104 case keyStringer: 105 return key.KeyString() 106 case fmt.Stringer: 107 return key.String() 108 default: 109 panic(fmt.Errorf("Unable to stringify type %T: %#v", key, key)) 110 } 111 } 112 113 // stringifyCollectionHelper is similar to StringifyInterface, but 114 // optimizes for human readability instead of making a unique string 115 // key suitable for JSON. 116 func stringifyCollectionHelper(val interface{}) string { 117 switch val := val.(type) { 118 case string: 119 return escape(val) 120 case map[string]interface{}: 121 keys := SortedKeys(val) 122 for i, k := range keys { 123 v := val[k] 124 s := stringifyCollectionHelper(v) 125 keys[i] = k + ":" + s 126 } 127 return "map[" + strings.Join(keys, " ") + "]" 128 case []interface{}: 129 elements := make([]string, len(val)) 130 for i, element := range val { 131 elements[i] = stringifyCollectionHelper(element) 132 } 133 return strings.Join(elements, ",") 134 case Pointer: 135 return "{" + val.Pointer().String() + "}" 136 case Path: 137 return "[" + val.String() + "]" 138 case Key: 139 return stringifyCollectionHelper(val.Key()) 140 } 141 142 return fmt.Sprint(val) 143 }