Skip to content

Commit

Permalink
allow decoding of nested structs
Browse files Browse the repository at this point in the history
  • Loading branch information
turbolent committed Jul 16, 2024
1 parent a46c2b1 commit 3fabbac
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 33 deletions.
96 changes: 63 additions & 33 deletions types.go
Original file line number Diff line number Diff line change
Expand Up @@ -504,40 +504,11 @@ func DecodeFields(composite Composite, s interface{}) error {
}

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

fieldsMap := FieldsMappedByName(composite)

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)
}

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

converted, err := decodeFieldValue(fieldValue.Type(), value)
if err != nil {
return fmt.Errorf(
"cannot convert Cadence field %s into Go field %s: %w",
cadenceFieldNameTag,
structField.Name,
err,
)
}

fieldValue.Set(converted)
_, err := decodeStructInto(v, targetType, composite)
if err != nil {
return err
}

return nil
Expand All @@ -553,6 +524,10 @@ func decodeFieldValue(targetType reflect.Type, value Value) (reflect.Value, erro
decodeSpecialFieldFunc = decodeDict
case reflect.Array, reflect.Slice:
decodeSpecialFieldFunc = decodeSlice
case reflect.Struct:
if !targetType.Implements(reflect.TypeOf((*Value)(nil)).Elem()) {
decodeSpecialFieldFunc = decodeStruct
}
}

var reflectedValue reflect.Value
Expand Down Expand Up @@ -709,6 +684,61 @@ func decodeSlice(arrayTargetType reflect.Type, cadenceValue Value) (reflect.Valu
return arrayValue, nil
}

func decodeStruct(structTargetType reflect.Type, cadenceValue Value) (reflect.Value, error) {
structValue := reflect.New(structTargetType)
return decodeStructInto(structValue.Elem(), structTargetType, cadenceValue)
}

func decodeStructInto(
structValue reflect.Value,
structTargetType reflect.Type,
cadenceValue Value,
) (reflect.Value, error) {
composite, ok := cadenceValue.(Composite)
if !ok {
return reflect.Value{}, fmt.Errorf(
"cannot decode non-Cadence composite %T to Go struct",
cadenceValue,
)
}

fieldsMap := FieldsMappedByName(composite)

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

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

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

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

converted, err := decodeFieldValue(fieldValue.Type(), value)
if err != nil {
return reflect.Value{}, fmt.Errorf(
"cannot convert Cadence field %s into Go field %s: %w",
cadenceFieldNameTag,
structField.Name,
err,
)
}

fieldValue.Set(converted)
}

return structValue, nil
}

// Parameter

type Parameter struct {
Expand Down
23 changes: 23 additions & 0 deletions types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2284,6 +2284,19 @@ func TestDecodeFields(t *testing.T) {
NewDictionary([]KeyValuePair{
{Key: UInt8(42), Value: UInt8(24)},
}),
NewStruct([]Value{
NewInt(42),
}).WithType(NewStructType(
utils.TestLocation,
"NestedStruct",
[]Field{
{
Identifier: "intField",
Type: IntType,
},
},
nil,
)),
},
).WithType(NewEventType(
utils.TestLocation,
Expand Down Expand Up @@ -2408,10 +2421,18 @@ func TestDecodeFields(t *testing.T) {
ElementType: UInt8Type,
},
},
{
Identifier: "goUint8Struct",
Type: AnyStructType,
},
},
nil,
))

type nestedStruct struct {
Int Int `cadence:"intField"`
}

type eventStruct struct {
Int Int `cadence:"intField"`
String String `cadence:"stringField"`
Expand All @@ -2433,6 +2454,7 @@ func TestDecodeFields(t *testing.T) {
GoUint8PtrSome *uint8 `cadence:"goUint8PtrSome"`
GoUint8Slice []uint8 `cadence:"goUint8Slice"`
GoUint8Map map[uint8]uint8 `cadence:"goUint8Map"`
GoUint8Struct nestedStruct `cadence:"goUint8Struct"`
NonCadenceField Int
}

Expand Down Expand Up @@ -2491,6 +2513,7 @@ func TestDecodeFields(t *testing.T) {
assert.Equal(t, &expectedUint8, evt.GoUint8PtrSome)
assert.Equal(t, []uint8{4, 2}, evt.GoUint8Slice)
assert.Equal(t, map[uint8]uint8{42: 24}, evt.GoUint8Map)
assert.Equal(t, NewInt(42), evt.GoUint8Struct.Int)

type ErrCases struct {
Value interface{}
Expand Down

0 comments on commit 3fabbac

Please sign in to comment.