Skip to content

Commit

Permalink
Merge pull request #2565 from rrrkren/eric/decodefieldstruct
Browse files Browse the repository at this point in the history
  • Loading branch information
turbolent authored Jun 12, 2023
2 parents 8448229 + 0a4254c commit 1c6994f
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 0 deletions.
50 changes: 50 additions & 0 deletions types.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package cadence

import (
"fmt"
"reflect"
"sync"

"github.com/onflow/cadence/runtime/common"
Expand Down Expand Up @@ -1110,6 +1111,55 @@ func GetFieldsMappedByName(v HasFields) map[string]Value {
return fieldsMap
}

// DecodeFields decodes a HasFields into a struct
func DecodeFields(hasFields HasFields, s interface{}) error {
v := reflect.ValueOf(s)
if v.Kind() != reflect.Ptr {
return fmt.Errorf("s must be a pointer to a struct")
}

v = v.Elem()
t := v.Type()

fieldsMap := GetFieldsMappedByName(hasFields)

for i := 0; i < v.NumField(); i++ {
structField := t.Field(i)
tag := structField.Tag
fieldValue := v.Field(i)

cadenceFieldNameTag := tag.Get("cadence")
if cadenceFieldNameTag == "" {
continue
}

if !fieldValue.IsValid() || !fieldValue.CanSet() {
return fmt.Errorf("cannot set field %s", structField.Name)
}

cadenceField := fieldsMap[cadenceFieldNameTag]
if cadenceField == nil {
return fmt.Errorf("%s field not found", cadenceFieldNameTag)
}

cadenceFieldValue := reflect.ValueOf(cadenceField)

if !cadenceFieldValue.CanConvert(fieldValue.Type()) {
return fmt.Errorf(
"cannot convert cadence field %s of type %s to struct field %s of type %s",
cadenceFieldNameTag,
cadenceField.Type().ID(),
structField.Name,
structField.Type.Name(),
)
}

fieldValue.Set(cadenceFieldValue.Convert(fieldValue.Type()))
}

return nil
}

// Parameter

type Parameter struct {
Expand Down
60 changes: 60 additions & 0 deletions types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/onflow/cadence/runtime/common"
"github.com/onflow/cadence/runtime/tests/utils"
Expand Down Expand Up @@ -1940,3 +1941,62 @@ func TestTypeEquality(t *testing.T) {
})

}

func TestDecodeFields(t *testing.T) {
simpleEvent := NewEvent(
[]Value{
NewInt(1),
String("foo"),
},
).WithType(&EventType{
Location: utils.TestLocation,
QualifiedIdentifier: "SimpleEvent",
Fields: []Field{
{
Identifier: "a",
Type: IntType{},
},
{
Identifier: "b",
Type: StringType{},
},
},
})

type eventStruct struct {
A Int `cadence:"a"`
B String `cadence:"b"`
NonCadenceField Int
}

evt := eventStruct{}
err := DecodeFields(simpleEvent, &evt)
require.NoError(t, err)
assert.EqualValues(t, eventStruct{
A: NewInt(1),
B: "foo",
NonCadenceField: Int{},
}, evt)

err = DecodeFields(simpleEvent, evt)
assert.Errorf(t, err, "should err when passing non-pointer")

type eventStructInvalidMapping struct {
A String `cadence:"a"`
}

err = DecodeFields(simpleEvent, &eventStructInvalidMapping{})
assert.Errorf(t, err, "should err when mapping to invalid type")

type eventStructPrivateField struct {
a Int `cadence:"a"` // nolint: unused
}
err = DecodeFields(simpleEvent, &eventStructPrivateField{})
assert.Errorf(t, err, "should err when mapping to private field")

type eventStructNotFoundField struct {
A Int `cadence:"c"`
}
err = DecodeFields(simpleEvent, &eventStructNotFoundField{})
assert.Errorf(t, err, "should err when mapping to non-existing field")
}

0 comments on commit 1c6994f

Please sign in to comment.