Skip to content

Commit

Permalink
WIP: Adjust Mapper/Bindings for Enums
Browse files Browse the repository at this point in the history
Signed-off-by: Dave Tucker <[email protected]>
  • Loading branch information
dave-tucker committed Jun 10, 2021
1 parent 23dd150 commit ad4f0bb
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 34 deletions.
5 changes: 3 additions & 2 deletions example/play_with_ovs/play_with_ovs.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ func play(ovs *client.OvsdbClient) {

func createBridge(ovs *client.OvsdbClient, bridgeName string) {
bridge := Bridge{
UUID: "gopher",
Name: bridgeName,
UUID: "gopher",
Name: bridgeName,
Protocols: []BridgeProtocols{BridgeProtocolsOpenflow14, BridgeProtocolsOpenflow15},
}
insertOp, err := ovs.Create(&bridge)
if err != nil {
Expand Down
31 changes: 26 additions & 5 deletions mapper/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,28 @@ func (mi *MapperInfo) SetField(column string, value interface{}) error {
return fmt.Errorf("column %s not found in orm info", column)
}
fieldValue := reflect.ValueOf(mi.obj).Elem().FieldByName(fieldName)

v := reflect.ValueOf(value)
if !fieldValue.Type().AssignableTo(reflect.TypeOf(value)) {
return fmt.Errorf("column %s: native value %v (%s) is not assignable to field %s (%s)",
column, value, reflect.TypeOf(value), fieldName, fieldValue.Type())
if v.Type().ConvertibleTo(fieldValue.Type()) {
// handle enum
v = v.Convert(fieldValue.Type())
} else if fieldValue.Kind() == reflect.Slice {
// handle set of enums
if !v.Type().Elem().ConvertibleTo(fieldValue.Type().Elem()) {
return fmt.Errorf("column %s: element %v (%s) is not convertible to field %s element (%s)",
column, value, reflect.TypeOf(value), fieldName, fieldValue.Type())
}
nv := reflect.Zero(fieldValue.Type())
for i := 0; i < v.Len(); i++ {
nv = reflect.Append(nv, v.Index(i).Convert(fieldValue.Type().Elem()))
}
v = nv
} else {
return fmt.Errorf("column %s: native value %v (%s) is not assignable or convertible to field %s (%s)",
column, value, reflect.TypeOf(value), fieldName, fieldValue.Type())
}
}
fieldValue.Set(reflect.ValueOf(value))
fieldValue.Set(v)
return nil
}

Expand Down Expand Up @@ -133,7 +149,12 @@ func NewMapperInfo(table *ovsdb.TableSchema, obj interface{}) (*MapperInfo, erro

// Perform schema-based type checking
expType := ovsdb.NativeType(column)
if expType != field.Type {
// check for slice of enums
if expType.Kind() == reflect.Slice && expType.Elem().Kind() == reflect.String {
// it's a slice of enums
} else if expType.Kind() == reflect.String && field.Type.Kind() == reflect.String {
// it' an enum
} else if expType != field.Type {
return nil, &ErrMapper{
objType: objType.String(),
field: field.Name,
Expand Down
90 changes: 73 additions & 17 deletions mapper/info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,57 @@ import (
)

var sampleTable = []byte(`{
"columns": {
"columns": {
"aString": {
"type": "string"
"type": "string"
},
"aInteger": {
"type": "integer"
"type": "integer"
},
"aSet": {
"type": {
"key": "string",
"max": "unlimited",
"min": 0
}
"type": {
"key": "string",
"max": "unlimited",
"min": 0
}
},
"aMap": {
"type": {
"key": "string",
"value": "string"
}
"type": {
"key": "string",
"value": "string"
}
},
"aEnum": {
"type": {
"key": {
"enum": [
"set",
[
"enum1",
"enum2",
"enum3"
]
],
"type": "string"
}
}
},
"aEnumSet": {
"type": {
"key": {
"enum": [
"set",
[
"enum1",
"enum2",
"enum3"
]
],
"type": "string"
},
"max": "unlimited",
"min": 0
}
}
}
}`)
Expand Down Expand Up @@ -73,11 +105,19 @@ func TestNewMapperInfo(t *testing.T) {
}

func TestMapperInfoSet(t *testing.T) {
type enum string
const (
enum1 enum = "one"
enum2 enum = "two"
enum3 enum = "three"
)
type obj struct {
Ostring string `ovs:"aString"`
Oint int `ovs:"aInteger"`
Oset []string `ovs:"aSet"`
Omap map[string]string `ovs:"aMap"`
Ostring string `ovs:"aString"`
Oint int `ovs:"aInteger"`
Oset []string `ovs:"aSet"`
Omap map[string]string `ovs:"aMap"`
Oenum enum `ovs:"aEnum"`
OenumSet []enum `ovs:"aEnumSet"`
}

type test struct {
Expand Down Expand Up @@ -127,13 +167,29 @@ func TestMapperInfoSet(t *testing.T) {
err: false,
},
{
name: "unassignalbe",
name: "unassignable",
table: sampleTable,
obj: &obj{},
field: []string{"foo"},
column: "aMap",
err: true,
},
{
name: "enum",
table: sampleTable,
obj: &obj{},
field: enum1,
column: "aEnum",
err: false,
},
{
name: "enumSet",
table: sampleTable,
obj: &obj{},
field: []enum{enum2, enum3},
column: "aEnumSet",
err: false,
},
}
for _, tt := range tests {
t.Run(fmt.Sprintf("SetField_%s", tt.name), func(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion modelgen/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ package {{ index . "PackageName" }}
{{ if index . "Enums" }}
type (
{{ range index . "Enums" }}
{{ .Alias }} = {{ .Type }}
{{ .Alias }} {{ .Type }}
{{- end }}
)
Expand Down
12 changes: 6 additions & 6 deletions modelgen/table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ func TestNewTableTemplate(t *testing.T) {
package test
type (
AtomicTableEventType = string
AtomicTableProtocol = string
AtomicTableEventType string
AtomicTableProtocol string
)
const (
Expand Down Expand Up @@ -91,8 +91,8 @@ type AtomicTable struct {
package test
type (
AtomicTableEventType = string
AtomicTableProtocol = string
AtomicTableEventType string
AtomicTableProtocol string
)
const (
Expand Down Expand Up @@ -140,8 +140,8 @@ func {{ index . "TestName" }} () string {
package test
type (
AtomicTableEventType = string
AtomicTableProtocol = string
AtomicTableEventType string
AtomicTableProtocol string
)
const (
Expand Down
23 changes: 20 additions & 3 deletions ovsdb/bindings.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,11 +168,28 @@ func OvsToNative(column *ColumnSchema, ovsElem interface{}) (interface{}, error)
// 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 {
rawElemType := reflect.TypeOf(rawElem)
if column.Type == TypeEnum && rawElemType != strType && rawElemType.Kind() == reflect.String {
// cast type alias back to string
rawElem = rawElem.(string)
rawElemType = reflect.TypeOf(rawElem)
}
if column.Type == TypeSet && len(column.TypeObj.Key.Enum) > 0 && rawElemType.Elem() != strType && rawElemType.Elem().Kind() == reflect.String {
// convert a set of enums where the type is an alias, to a set of strings
tmp := []string{}
v := reflect.ValueOf(rawElem)
for i := 0; i < v.Len(); i++ {
// convert value to string first, because the String method won't panic
// but will instead return a string of <T value> which isn't what we want
vi := v.Index(i).Convert(strType)
tmp = append(tmp, vi.String())
}
rawElem = tmp
rawElemType = reflect.TypeOf(rawElem)
}
if t := rawElemType; t != naType {
return nil, NewErrWrongType("NativeToOvs", naType.String(), rawElem)
}

switch column.Type {
case TypeInteger, TypeReal, TypeString, TypeBoolean, TypeEnum:
return rawElem, nil
Expand Down

0 comments on commit ad4f0bb

Please sign in to comment.