forked from ovn-kubernetes/libovsdb
-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathbindings.go
214 lines (195 loc) · 5.91 KB
/
bindings.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
package libovsdb
import (
"fmt"
"reflect"
)
var (
intType = reflect.TypeOf(0)
realType = reflect.TypeOf(0.0)
boolType = reflect.TypeOf(true)
strType = reflect.TypeOf("")
)
// ErrWrongType describes typing error
type ErrWrongType struct {
from string
expected string
got interface{}
}
func (e *ErrWrongType) Error() string {
return fmt.Sprintf("Wrong Type (%s): expected %s but got %s (%s)",
e.from, e.expected, e.got, reflect.TypeOf(e.got))
}
// NewErrWrongType creates a new ErrWrongType
func NewErrWrongType(from, expected string, got interface{}) error {
return &ErrWrongType{
from: from,
expected: expected,
got: got,
}
}
// nativeTypeFromBasic returns the native type that can hold a value of an
// BasicType type
func nativeTypeFromBasic(basicType string) reflect.Type {
switch basicType {
case TypeInteger:
return intType
case TypeReal:
return realType
case TypeBoolean:
return boolType
case TypeString:
return strType
case TypeUUID:
return strType
default:
panic("Unkown basic type %s basicType")
}
}
// nativeValueOf returns the native value of the atomic element
// Usually, this is just reflect.ValueOf(elem), with the only exception of the UUID
func nativeValueOf(elem interface{}, elemType ExtendedType) (reflect.Value, error) {
if elemType == TypeUUID {
uuid, ok := elem.(UUID)
if !ok {
return reflect.ValueOf(nil), NewErrWrongType("nativeValueOf", "UUID", elem)
}
return reflect.ValueOf(uuid.GoUUID), nil
}
return reflect.ValueOf(elem), nil
}
//nativeType returns the reflect.Type that can hold the value of a column
//OVS Type to Native Type convertions:
// OVS sets -> go slices
// OVS uuid -> go strings
// OVS map -> go map
// OVS enum -> go native type depending on the type of the enum key
func nativeType(column *ColumnSchema) reflect.Type {
switch column.Type {
case TypeInteger, TypeReal, TypeBoolean, TypeUUID, TypeString:
return nativeTypeFromBasic(column.Type)
case TypeEnum:
return nativeTypeFromBasic(column.TypeObj.Key.Type)
case TypeMap:
kType := nativeTypeFromBasic(column.TypeObj.Key.Type)
vType := nativeTypeFromBasic(column.TypeObj.Value.Type)
return reflect.MapOf(kType, vType)
case TypeSet:
kType := nativeTypeFromBasic(column.TypeObj.Key.Type)
return reflect.SliceOf(kType)
default:
panic(fmt.Errorf("Unknown Extended type %s", column.Type))
}
}
// OvsToNative transforms an ovs type to native one based on the column type information
func OvsToNative(column *ColumnSchema, ovsElem interface{}) (interface{}, error) {
naType := nativeType(column)
switch column.Type {
case TypeInteger, TypeReal, TypeString, TypeBoolean, TypeEnum:
if reflect.TypeOf(ovsElem) != naType {
return nil, NewErrWrongType("OvsToNative", naType.String(), ovsElem)
}
// Atomic types should have the same underlying type
return ovsElem, nil
case TypeUUID:
uuid, ok := ovsElem.(UUID)
if !ok {
return nil, NewErrWrongType("OvsToNative", "UUID", ovsElem)
}
return uuid.GoUUID, nil
case TypeSet:
// The inner slice is []interface{}
// We need to convert it to the real type os slice
var nativeSet reflect.Value
// RFC says that for a set of exactly one, an atomic type an be sent
switch ovsElem.(type) {
case OvsSet:
ovsSet := ovsElem.(OvsSet)
nativeSet = reflect.MakeSlice(naType, 0, len(ovsSet.GoSet))
for _, v := range ovsSet.GoSet {
vv, err := nativeValueOf(v, column.TypeObj.Key.Type)
if err != nil {
return nil, err
}
if vv.Type() != naType.Elem() {
return nil, NewErrWrongType("OvsToNative", fmt.Sprintf("convertible to %s", naType), ovsElem)
}
nativeSet = reflect.Append(nativeSet, vv)
}
default:
nativeSet = reflect.MakeSlice(naType, 0, 1)
keyType := nativeTypeFromBasic(column.TypeObj.Key.Type)
vv, err := nativeValueOf(ovsElem, column.TypeObj.Key.Type)
if err != nil {
return nil, err
}
if !vv.Type().ConvertibleTo(keyType) {
return nil, NewErrWrongType("OvsToNative", keyType.String(), ovsElem)
}
nativeSet = reflect.Append(nativeSet, vv)
}
return nativeSet.Interface(), nil
case TypeMap:
ovsMap, ok := ovsElem.(OvsMap)
if !ok {
return nil, NewErrWrongType("OvsToNative", "OvsMap", ovsElem)
}
// The inner slice is map[interface]interface{}
// We need to convert it to the real type os slice
nativeMap := reflect.MakeMapWithSize(naType, len(ovsMap.GoMap))
for k, v := range ovsMap.GoMap {
kk, err := nativeValueOf(k, column.TypeObj.Key.Type)
if err != nil {
return nil, err
}
vv, err := nativeValueOf(v, column.TypeObj.Value.Type)
if err != nil {
return nil, err
}
if vv.Type() != naType.Elem() || kk.Type() != naType.Key() {
return nil, NewErrWrongType("OvsToNative", fmt.Sprintf("convertible to %s", naType), ovsElem)
}
nativeMap.SetMapIndex(kk, vv)
}
return nativeMap.Interface(), nil
default:
panic(fmt.Sprintf("Unknown Type: %v", column.Type))
}
}
// NativeToOvs transforms an native type to a ovs type based on the column type information
func NativeToOvs(column *ColumnSchema, rawElem interface{}) (interface{}, error) {
naType := nativeType(column)
if t := reflect.TypeOf(rawElem); t != naType {
return nil, NewErrWrongType("NativeToOvs", naType.String(), rawElem)
}
switch column.Type {
case TypeInteger, TypeReal, TypeString, TypeBoolean, TypeEnum:
return rawElem, nil
case TypeUUID:
return UUID{GoUUID: rawElem.(string)}, nil
case TypeSet:
var ovsSet *OvsSet
if column.TypeObj.Key.Type == TypeUUID {
var ovsSlice []interface{}
for _, v := range rawElem.([]string) {
uuid := UUID{GoUUID: v}
ovsSlice = append(ovsSlice, uuid)
}
ovsSet = &OvsSet{GoSet: ovsSlice}
} else {
var err error
ovsSet, err = NewOvsSet(rawElem)
if err != nil {
return nil, err
}
}
return ovsSet, nil
case TypeMap:
ovsMap, err := NewOvsMap(rawElem)
if err != nil {
return nil, err
}
return ovsMap, nil
default:
panic(fmt.Sprintf("Unknown Type: %v", column.Type))
}
}