Skip to content

Commit

Permalink
hard coder modifier updated
Browse files Browse the repository at this point in the history
  • Loading branch information
EasterTheBunny committed Jan 23, 2025
1 parent c4128ed commit 5c868fb
Show file tree
Hide file tree
Showing 12 changed files with 105 additions and 53 deletions.
33 changes: 31 additions & 2 deletions pkg/codec/by_item_type_modifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package codec

import (
"fmt"
"log"
"reflect"

"github.com/smartcontractkit/chainlink-common/pkg/types"
Expand All @@ -15,20 +16,43 @@ func NewByItemTypeModifier(modByItemType map[string]Modifier) (Modifier, error)

return &byItemTypeModifier{
modByitemType: modByItemType,
enableNesting: false,
}, nil
}

// NewNestableByItemTypeModifier returns a Modifier that uses modByItemType to determine which Modifier to use for a
// given itemType. If itemType is structured as a dot-separated string like 'A.B.C', the first part 'A' will be used to
// match in the mod map and the remaining list will be provided to the found Modifier 'B.C'.
func NewNestableByItemTypeModifier(modByItemType map[string]Modifier) (Modifier, error) {
if modByItemType == nil {
modByItemType = map[string]Modifier{}
}

return &byItemTypeModifier{
modByitemType: modByItemType,
enableNesting: true,
}, nil
}

type byItemTypeModifier struct {
modByitemType map[string]Modifier
enableNesting bool
}

// RetypeToOffChain attempts to apply a modifier using the provided itemType. To allow access to nested fields, this
// function applies no modifications if a modifier by the specified name is not found.
func (b *byItemTypeModifier) RetypeToOffChain(onChainType reflect.Type, itemType string) (reflect.Type, error) {
head, tail := extendedItemType(itemType).next()
head := itemType
tail := itemType

if b.enableNesting {
head, tail = ItemTyper(itemType).Next()
}
log.Println("byItemTypeModifier", "RetypeToOffChain", onChainType, head, ":", tail)

mod, ok := b.modByitemType[head]
if !ok {
log.Println(mod)
return nil, fmt.Errorf("%w: cannot find modifier for %s", types.ErrInvalidType, itemType)
}

Expand All @@ -48,7 +72,12 @@ func (b *byItemTypeModifier) transform(
itemType string,
transform func(Modifier, any, string) (any, error),
) (any, error) {
head, tail := extendedItemType(itemType).next()
head := itemType
tail := itemType

if b.enableNesting {
head, tail = ItemTyper(itemType).Next()
}

if mod, ok := b.modByitemType[head]; ok {
return transform(mod, val, tail)
Expand Down
4 changes: 2 additions & 2 deletions pkg/codec/byte_string_modifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,12 @@ func (t *bytesToStringModifier) RetypeToOffChain(onChainType reflect.Type, _ str

// TransformToOnChain uses the AddressModifier for string-to-address conversion.
func (t *bytesToStringModifier) TransformToOnChain(offChainValue any, _ string) (any, error) {
return transformWithMaps(offChainValue, t.offToOnChainType, t.fields, noop, stringToAddressHookForOnChain(t.modifier))
return transformWithMaps(offChainValue, t.offToOnChainTyper, "", t.fields, noop, stringToAddressHookForOnChain(t.modifier))
}

// TransformToOffChain uses the AddressModifier for address-to-string conversion.
func (t *bytesToStringModifier) TransformToOffChain(onChainValue any, _ string) (any, error) {
return transformWithMaps(onChainValue, t.onToOffChainType, t.fields,
return transformWithMaps(onChainValue, t.onToOffChainTyper, "", t.fields,
addressTransformationAction(t.modifier.Length()),
addressToStringHookForOffChain(t.modifier),
)
Expand Down
4 changes: 2 additions & 2 deletions pkg/codec/element_extractor.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@ type elementExtractor struct {
}

func (e *elementExtractor) TransformToOnChain(offChainValue any, _ string) (any, error) {
return transformWithMaps(offChainValue, e.offToOnChainType, e.fields, extractMap)
return transformWithMaps(offChainValue, e.offToOnChainTyper, "", e.fields, extractMap)
}

func (e *elementExtractor) TransformToOffChain(onChainValue any, _ string) (any, error) {
return transformWithMaps(onChainValue, e.onToOffChainType, e.fields, expandMap)
return transformWithMaps(onChainValue, e.onToOffChainTyper, "", e.fields, expandMap)
}

func extractMap(extractMap map[string]any, key string, elementLocation *ElementExtractorLocation) error {
Expand Down
21 changes: 2 additions & 19 deletions pkg/codec/encodings/struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package encodings
import (
"fmt"
"reflect"
"strings"

"github.com/smartcontractkit/chainlink-common/pkg/codec"
"github.com/smartcontractkit/chainlink-common/pkg/types"
)

Expand Down Expand Up @@ -122,10 +122,8 @@ func (s *structCodec) SizeAtTopLevel(numItems int) (int, error) {
}

func (s *structCodec) FieldCodec(itemType string) (TypeCodec, error) {
path := extendedItemType(itemType)

// itemType could recurse into nested structs
fieldName, tail := path.next()
fieldName, tail := codec.ItemTyper(itemType).Next()
if fieldName == "" {
return nil, fmt.Errorf("%w: field name required", types.ErrInvalidType)
}
Expand All @@ -148,18 +146,3 @@ func (s *structCodec) FieldCodec(itemType string) (TypeCodec, error) {

return structType.FieldCodec(tail)
}

type extendedItemType string

func (t extendedItemType) next() (string, string) {
if string(t) == "" {
return "", ""
}

path := strings.Split(string(t), ".")
if len(path) == 1 {
return path[0], ""
}

return path[0], strings.Join(path[1:], ".")
}
5 changes: 1 addition & 4 deletions pkg/codec/encodings/type_codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,7 @@ func decode(c map[string]TypeCodec, raw []byte, into any, itemType string, exact

func getCodec(c map[string]TypeCodec, itemType string) (TypeCodec, error) {
// itemType could recurse into nested structs
path := extendedItemType(itemType)

// itemType could recurse into nested structs
head, tail := path.next()
head, tail := codec.ItemTyper(itemType).Next()
if head == "" {
return nil, fmt.Errorf("%w: cannot find type %s", types.ErrInvalidType, itemType)
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/codec/epoch_to_time.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ type timeToUnixModifier struct {

func (t *timeToUnixModifier) TransformToOnChain(offChainValue any, itemType string) (any, error) {
// since the hook will convert time.Time to epoch, we don't need to worry about converting them in the maps
return transformWithMaps(offChainValue, t.offToOnChainType, t.fields, noop, EpochToTimeHook, BigIntHook)
return transformWithMaps(offChainValue, t.offToOnChainTyper, "", t.fields, noop, EpochToTimeHook, BigIntHook)
}

func (t *timeToUnixModifier) TransformToOffChain(onChainValue any, itemType string) (any, error) {
// since the hook will convert epoch to time.Time, we don't need to worry about converting them in the maps
return transformWithMaps(onChainValue, t.onToOffChainType, t.fields, noop, EpochToTimeHook, BigIntHook)
return transformWithMaps(onChainValue, t.onToOffChainTyper, "", t.fields, noop, EpochToTimeHook, BigIntHook)
}

func noop(_ map[string]any, _ string, _ bool) error {
Expand Down
8 changes: 3 additions & 5 deletions pkg/codec/hard_coder.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package codec

import (
"fmt"
"log"
"reflect"
"strings"

Expand Down Expand Up @@ -83,15 +82,14 @@ func verifyHardCodeKeys(values map[string]any) error {
}

func (o *onChainHardCoder) TransformToOnChain(offChainValue any, itemType string) (any, error) {
log.Println(itemType)
return transformWithMaps(offChainValue, o.offToOnChainType, o.onChain, hardCode, o.hooks...)
return transformWithMaps(offChainValue, o.offToOnChainTyper, itemType, o.onChain, hardCode, o.hooks...)
}

func (o *onChainHardCoder) TransformToOffChain(onChainValue any, _ string) (any, error) {
func (o *onChainHardCoder) TransformToOffChain(onChainValue any, itemType string) (any, error) {
allHooks := make([]mapstructure.DecodeHookFunc, len(o.hooks)+1)
copy(allHooks, o.hooks)
allHooks[len(o.hooks)] = hardCodeManyHook
return transformWithMaps(onChainValue, o.onToOffChainType, o.fields, hardCode, allHooks...)
return transformWithMaps(onChainValue, o.onToOffChainTyper, itemType, o.fields, hardCode, allHooks...)
}

func hardCode(extractMap map[string]any, key string, item any) error {
Expand Down
46 changes: 46 additions & 0 deletions pkg/codec/hard_coder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,52 @@ func TestHardCoder(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, int32(123), reflect.ValueOf(offChain).FieldByName("B").Interface())
})

t.Run("TransformToOnChain and TransformToOffChain works for itemType path", func(t *testing.T) {
nestedHardCoder, err := codec.NewHardCoder(map[string]any{
"A": "Top",
"B.A": "Foo",
"B.C": []int32{2, 3},
"C.A": "Foo",
"C.C": []int32{2, 3},
}, map[string]any{
"B.Z": "Bar",
"B.Q": []struct {
A int
B string
}{{1, "a"}, {2, "b"}},
"C.Z": "Bar",
"C.Q": []struct {
A int
B string
}{{1, "a"}, {2, "b"}},
})
require.NoError(t, err)

offChainType, err := nestedHardCoder.RetypeToOffChain(reflect.TypeOf(nestedTestStruct{}), "")
require.NoError(t, err)

_, err = nestedHardCoder.RetypeToOffChain(reflect.TypeOf(""), "B.A")
require.NoError(t, err)

iInput := reflect.Indirect(reflect.New(offChainType))
iB := iInput.FieldByName("B")
iB.FieldByName("B").SetInt(1)
iC := iInput.FieldByName("C")
iC.Set(reflect.MakeSlice(iC.Type(), 2, 2))
iC.Index(0).FieldByName("B").SetInt(2)
iC.Index(1).FieldByName("B").SetInt(3)
iInput.FieldByName("D").SetInt(1)

actual, err := nestedHardCoder.TransformToOnChain(iInput.FieldByName("B").FieldByName("A").Interface(), "B.A")
require.NoError(t, err)

expected := "Foo"
assert.Equal(t, expected, actual)

_, err = nestedHardCoder.TransformToOffChain(expected, "B.A")
require.NoError(t, err)
})
}

// Since we're using the on-chain values that have their hard-coded values set to
Expand Down
22 changes: 12 additions & 10 deletions pkg/codec/modifier_base.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,18 +194,19 @@ type mapAction[T any] func(extractMap map[string]any, key string, element T) err

func transformWithMaps[T any](
item any,
typeMap map[reflect.Type]reflect.Type,
typeFn func(offChainType reflect.Type, itemType string) (reflect.Type, error),
itemType string,
fields map[string]T,
fn mapAction[T],
hooks ...mapstructure.DecodeHookFunc) (any, error) {
rItem := reflect.ValueOf(item)

toType, ok := typeMap[rItem.Type()]
if !ok {
return reflect.Value{}, fmt.Errorf("%w: cannot retype %v", types.ErrInvalidType, rItem.Type())
toType, err := typeFn(rItem.Type(), itemType)
if err != nil {
return reflect.Value{}, err
}

rOutput, err := transformWithMapsHelper(rItem, toType, fields, fn, hooks)
rOutput, err := transformWithMapsHelper(rItem, toType, itemType, fields, fn, hooks)
if err != nil {
return reflect.Value{}, err
}
Expand All @@ -216,6 +217,7 @@ func transformWithMaps[T any](
func transformWithMapsHelper[T any](
rItem reflect.Value,
toType reflect.Type,
itemType string,
fields map[string]T,
fn mapAction[T],
hooks []mapstructure.DecodeHookFunc) (reflect.Value, error) {
Expand All @@ -229,7 +231,7 @@ func transformWithMapsHelper[T any](
return into, err
}

tmp, err := transformWithMapsHelper(elm, toType.Elem(), fields, fn, hooks)
tmp, err := transformWithMapsHelper(elm, toType.Elem(), itemType, fields, fn, hooks)
result := reflect.New(toType.Elem())
reflect.Indirect(result).Set(tmp)

Expand Down Expand Up @@ -262,7 +264,7 @@ func doMany[T any](rInput, rOutput reflect.Value, fields map[string]T, fn mapAct
inTmp := rInput.Index(i)
outTmp := rOutput.Index(i)

output, err := transformWithMapsHelper(inTmp, outTmp.Type(), fields, fn, hooks)
output, err := transformWithMapsHelper(inTmp, outTmp.Type(), "", fields, fn, hooks)
if err != nil {
return err
}
Expand Down Expand Up @@ -337,7 +339,7 @@ func typeForPath(from reflect.Type, itemType string) (reflect.Type, error) {
case reflect.Array, reflect.Slice:
return nil, fmt.Errorf("%w: cannot extract a field from an array or slice", types.ErrInvalidType)
case reflect.Struct:
head, tail := extendedItemType(itemType).next()
head, tail := ItemTyper(itemType).Next()

field, ok := from.FieldByName(head)
if !ok {
Expand Down Expand Up @@ -367,9 +369,9 @@ func (e PathMappingError) Cause() error {
return e.Err
}

type extendedItemType string
type ItemTyper string

func (t extendedItemType) next() (string, string) {
func (t ItemTyper) Next() (string, string) {
if string(t) == "" {
return "", ""
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/codec/precodec.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func (pc *preCodec) TransformToOffChain(onChainValue any, _ string) (any, error)
allHooks := make([]mapstructure.DecodeHookFunc, 1)
allHooks[0] = hardCodeManyHook

return transformWithMaps(onChainValue, pc.onToOffChainType, pc.fields, pc.decodeFieldMapAction, allHooks...)
return transformWithMaps(onChainValue, pc.onToOffChainTyper, "", pc.fields, pc.decodeFieldMapAction, allHooks...)
}

func (pc *preCodec) decodeFieldMapAction(extractMap map[string]any, key string, typeDef string) error {
Expand Down Expand Up @@ -90,7 +90,7 @@ func (pc *preCodec) TransformToOnChain(offChainValue any, _ string) (any, error)
allHooks := make([]mapstructure.DecodeHookFunc, 1)
allHooks[0] = hardCodeManyHook

return transformWithMaps(offChainValue, pc.offToOnChainType, pc.fields, pc.encodeFieldMapAction, allHooks...)
return transformWithMaps(offChainValue, pc.offToOnChainTyper, "", pc.fields, pc.encodeFieldMapAction, allHooks...)
}

func (pc *preCodec) encodeFieldMapAction(extractMap map[string]any, key string, typeDef string) error {
Expand Down
3 changes: 0 additions & 3 deletions pkg/codec/renamer.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package codec

import (
"fmt"
"log"
"reflect"
"strings"
"unicode"
Expand Down Expand Up @@ -63,9 +62,7 @@ func (r *renamer) TransformToOffChain(onChainValue any, itemType string) (any, e
}

func (r *renamer) TransformToOnChain(offChainValue any, itemType string) (any, error) {
log.Println(itemType)
if itemType != "" {
log.Println(itemType)
var ref string

parts := strings.Split(itemType, ".")
Expand Down
4 changes: 2 additions & 2 deletions pkg/codec/wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ type wrapperModifier struct {
}

func (t *wrapperModifier) TransformToOnChain(offChainValue any, _ string) (any, error) {
return transformWithMaps(offChainValue, t.offToOnChainType, t.fields, unwrapFieldMapAction)
return transformWithMaps(offChainValue, t.offToOnChainTyper, "", t.fields, unwrapFieldMapAction)
}

func (t *wrapperModifier) TransformToOffChain(onChainValue any, _ string) (any, error) {
return transformWithMaps(onChainValue, t.onToOffChainType, t.fields, wrapFieldMapAction)
return transformWithMaps(onChainValue, t.onToOffChainTyper, "", t.fields, wrapFieldMapAction)
}

func wrapFieldMapAction(typesMap map[string]any, fieldName string, wrappedFieldName string) error {
Expand Down

0 comments on commit 5c868fb

Please sign in to comment.