/
go_to_value.go
226 lines (185 loc) · 5.14 KB
/
go_to_value.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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package encoding
import (
"errors"
"fmt"
"reflect"
"strings"
"unicode"
sdk "github.com/hashicorp/sentinel-sdk"
proto "github.com/hashicorp/sentinel-sdk/proto/go"
)
// GoToValue converts the Go value to a protobuf Object.
//
// The Go value must contain only primitives, collections of primitives,
// and structures. It must not contain any other type of value or an error
// will be returned.
//
// The primitive types byte and rune are aliases to integer types (as
// defined by the Go spec) and are treated as integers in conversion.
func GoToValue(raw interface{}) (*proto.Value, error) {
return toValue_reflect(reflect.ValueOf(raw))
}
func toValue_reflect(v reflect.Value) (*proto.Value, error) {
// Null pointer
if !v.IsValid() {
return &proto.Value{Type: proto.Value_NULL}, nil
}
// Decode depending on the type. We need to redo all of the primitives
// above unfortunately since they may fall to this point if they're
// wrapped in an interface type.
switch v.Kind() {
case reflect.Interface:
return toValue_reflect(v.Elem())
case reflect.Ptr:
switch v.Interface() {
case sdk.Null:
return &proto.Value{Type: proto.Value_NULL}, nil
case sdk.Undefined:
return &proto.Value{Type: proto.Value_UNDEFINED}, nil
}
return toValue_reflect(v.Elem())
case reflect.Bool:
return &proto.Value{
Type: proto.Value_BOOL,
Value: &proto.Value_ValueBool{ValueBool: v.Bool()},
}, nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return &proto.Value{
Type: proto.Value_INT,
Value: &proto.Value_ValueInt{ValueInt: v.Int()},
}, nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return &proto.Value{
Type: proto.Value_INT,
Value: &proto.Value_ValueInt{ValueInt: int64(v.Uint())},
}, nil
case reflect.Float32, reflect.Float64:
return &proto.Value{
Type: proto.Value_FLOAT,
Value: &proto.Value_ValueFloat{ValueFloat: v.Float()},
}, nil
case reflect.Complex64, reflect.Complex128:
return nil, errors.New("cannot convert complex number to Sentinel value")
case reflect.String:
return &proto.Value{
Type: proto.Value_STRING,
Value: &proto.Value_ValueString{ValueString: v.String()},
}, nil
case reflect.Array, reflect.Slice:
return toValue_array(v)
case reflect.Map:
return toValue_map(v)
case reflect.Struct:
return toValue_struct(v)
case reflect.Chan:
return nil, errors.New("cannot convert channel to Sentinel value")
case reflect.Func:
return nil, errors.New("cannot convert func to Sentinel value")
}
return nil, fmt.Errorf("cannot convert type %s to Sentinel value", v.Kind())
}
func toValue_array(v reflect.Value) (*proto.Value, error) {
vs := make([]*proto.Value, v.Len())
for i := range vs {
elem, err := toValue_reflect(v.Index(i))
if err != nil {
return nil, err
}
vs[i] = elem
}
return &proto.Value{
Type: proto.Value_LIST,
Value: &proto.Value_ValueList{
ValueList: &proto.Value_List{
Elems: vs,
},
},
}, nil
}
func toValue_map(v reflect.Value) (*proto.Value, error) {
vs := make([]*proto.Value_KV, v.Len())
for i, keyV := range v.MapKeys() {
key, err := toValue_reflect(keyV)
if err != nil {
return nil, err
}
value, err := toValue_reflect(v.MapIndex(keyV))
if err != nil {
return nil, err
}
vs[i] = &proto.Value_KV{
Key: key,
Value: value,
}
}
return &proto.Value{
Type: proto.Value_MAP,
Value: &proto.Value_ValueMap{
ValueMap: &proto.Value_Map{
Elems: vs,
},
},
}, nil
}
func toValue_struct(v reflect.Value) (*proto.Value, error) {
// Get the type since we need this to determine what is exported,
// field tags, etc.
t := v.Type()
vs := make([]*proto.Value_KV, 0, v.NumField())
for i := 0; i < v.NumField(); i++ {
field := t.Field(i)
// If PkgPath is non-empty, this is unexported and can be ignored
if field.PkgPath != "" {
continue
}
// Determine the map key
key := toValue_struct_fieldName([]rune(field.Name))
if v, ok := field.Tag.Lookup("sentinel"); ok {
// A blank value means to not export this value
if v == "" {
continue
}
key = v
}
// Convert the value
value, err := toValue_reflect(v.Field(i))
if err != nil {
return nil, err
}
vs = append(vs, &proto.Value_KV{
Value: value,
Key: &proto.Value{
Type: proto.Value_STRING,
Value: &proto.Value_ValueString{ValueString: key},
},
})
}
return &proto.Value{
Type: proto.Value_MAP,
Value: &proto.Value_ValueMap{
ValueMap: &proto.Value_Map{
Elems: vs,
},
},
}, nil
}
func toValue_struct_fieldName(s []rune) string {
var result []string
var last int
// Always convert the zero-index rune to a lowercase letter. Since we always
// operate on exported struct fields, this is fine and is actually less
// costly than doing an IsUpper first.
s[0] = unicode.ToLower(s[0])
for idx := 1; idx < len(s); idx++ {
if unicode.IsUpper(s[idx]) {
result = append(result, string(s[last:idx]))
last = idx
s[idx] = unicode.ToLower(s[idx])
}
}
// Append anything remaining
result = append(result, string(s[last:]))
return strings.Join(result, "_")
}