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