1package godo
2
3import (
4	"bytes"
5	"fmt"
6	"io"
7	"reflect"
8	"strings"
9)
10
11var timestampType = reflect.TypeOf(Timestamp{})
12
13// ResourceWithURN is an interface for interfacing with the types
14// that implement the URN method.
15type ResourceWithURN interface {
16	URN() string
17}
18
19// ToURN converts the resource type and ID to a valid DO API URN.
20func ToURN(resourceType string, id interface{}) string {
21	return fmt.Sprintf("%s:%s:%v", "do", strings.ToLower(resourceType), id)
22}
23
24// Stringify attempts to create a string representation of DigitalOcean types
25func Stringify(message interface{}) string {
26	var buf bytes.Buffer
27	v := reflect.ValueOf(message)
28	stringifyValue(&buf, v)
29	return buf.String()
30}
31
32// stringifyValue was graciously cargoculted from the goprotubuf library
33func stringifyValue(w io.Writer, val reflect.Value) {
34	if val.Kind() == reflect.Ptr && val.IsNil() {
35		_, _ = w.Write([]byte("<nil>"))
36		return
37	}
38
39	v := reflect.Indirect(val)
40
41	switch v.Kind() {
42	case reflect.String:
43		fmt.Fprintf(w, `"%s"`, v)
44	case reflect.Slice:
45		stringifySlice(w, v)
46		return
47	case reflect.Struct:
48		stringifyStruct(w, v)
49	default:
50		if v.CanInterface() {
51			fmt.Fprint(w, v.Interface())
52		}
53	}
54}
55
56func stringifySlice(w io.Writer, v reflect.Value) {
57	_, _ = w.Write([]byte{'['})
58	for i := 0; i < v.Len(); i++ {
59		if i > 0 {
60			_, _ = w.Write([]byte{' '})
61		}
62
63		stringifyValue(w, v.Index(i))
64	}
65
66	_, _ = w.Write([]byte{']'})
67}
68
69func stringifyStruct(w io.Writer, v reflect.Value) {
70	if v.Type().Name() != "" {
71		_, _ = w.Write([]byte(v.Type().String()))
72	}
73
74	// special handling of Timestamp values
75	if v.Type() == timestampType {
76		fmt.Fprintf(w, "{%s}", v.Interface())
77		return
78	}
79
80	_, _ = w.Write([]byte{'{'})
81
82	var sep bool
83	for i := 0; i < v.NumField(); i++ {
84		fv := v.Field(i)
85		if fv.Kind() == reflect.Ptr && fv.IsNil() {
86			continue
87		}
88		if fv.Kind() == reflect.Slice && fv.IsNil() {
89			continue
90		}
91
92		if sep {
93			_, _ = w.Write([]byte(", "))
94		} else {
95			sep = true
96		}
97
98		_, _ = w.Write([]byte(v.Type().Field(i).Name))
99		_, _ = w.Write([]byte{':'})
100		stringifyValue(w, fv)
101	}
102
103	_, _ = w.Write([]byte{'}'})
104}
105