-
Notifications
You must be signed in to change notification settings - Fork 300
/
strings.go
128 lines (106 loc) · 2.57 KB
/
strings.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
package godo
import (
"bytes"
"fmt"
"io"
"reflect"
"sort"
"strings"
)
var timestampType = reflect.TypeOf(Timestamp{})
// ResourceWithURN is an interface for interfacing with the types
// that implement the URN method.
type ResourceWithURN interface {
URN() string
}
// ToURN converts the resource type and ID to a valid DO API URN.
func ToURN(resourceType string, id interface{}) string {
return fmt.Sprintf("%s:%s:%v", "do", strings.ToLower(resourceType), id)
}
// Stringify attempts to create a string representation of DigitalOcean types
func Stringify(message interface{}) string {
var buf bytes.Buffer
v := reflect.ValueOf(message)
stringifyValue(&buf, v)
return buf.String()
}
// stringifyValue was graciously cargoculted from the goprotubuf library
func stringifyValue(w io.Writer, val reflect.Value) {
if val.Kind() == reflect.Ptr && val.IsNil() {
_, _ = w.Write([]byte("<nil>"))
return
}
v := reflect.Indirect(val)
switch v.Kind() {
case reflect.String:
fmt.Fprintf(w, `"%s"`, v)
case reflect.Slice:
stringifySlice(w, v)
return
case reflect.Struct:
stringifyStruct(w, v)
case reflect.Map:
stringifyMap(w, v)
default:
if v.CanInterface() {
fmt.Fprint(w, v.Interface())
}
}
}
func stringifySlice(w io.Writer, v reflect.Value) {
_, _ = w.Write([]byte{'['})
for i := 0; i < v.Len(); i++ {
if i > 0 {
_, _ = w.Write([]byte{' '})
}
stringifyValue(w, v.Index(i))
}
_, _ = w.Write([]byte{']'})
}
func stringifyMap(w io.Writer, v reflect.Value) {
_, _ = w.Write([]byte("map["))
// Sort the keys so that the output is stable
keys := v.MapKeys()
sort.Slice(keys, func(i, j int) bool {
return fmt.Sprintf("%v", keys[i]) < fmt.Sprintf("%v", keys[j])
})
for i, key := range keys {
stringifyValue(w, key)
_, _ = w.Write([]byte{':'})
stringifyValue(w, v.MapIndex(key))
if i < len(keys)-1 {
_, _ = w.Write([]byte(", "))
}
}
_, _ = w.Write([]byte("]"))
}
func stringifyStruct(w io.Writer, v reflect.Value) {
if v.Type().Name() != "" {
_, _ = w.Write([]byte(v.Type().String()))
}
// special handling of Timestamp values
if v.Type() == timestampType {
fmt.Fprintf(w, "{%s}", v.Interface())
return
}
_, _ = w.Write([]byte{'{'})
var sep bool
for i := 0; i < v.NumField(); i++ {
fv := v.Field(i)
if fv.Kind() == reflect.Ptr && fv.IsNil() {
continue
}
if fv.Kind() == reflect.Slice && fv.IsNil() {
continue
}
if sep {
_, _ = w.Write([]byte(", "))
} else {
sep = true
}
_, _ = w.Write([]byte(v.Type().Field(i).Name))
_, _ = w.Write([]byte{':'})
stringifyValue(w, fv)
}
_, _ = w.Write([]byte{'}'})
}