From 55826ae67136e3b7d2d3e63a11fbf07ef00143cf Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Mon, 13 May 2024 07:47:03 -0700 Subject: [PATCH 1/2] Reinterpret map as struct --- functions/assemblyscript/values.go | 102 +++++++++++++++++++++++------ 1 file changed, 81 insertions(+), 21 deletions(-) diff --git a/functions/assemblyscript/values.go b/functions/assemblyscript/values.go index 3465cfb2..457bb961 100644 --- a/functions/assemblyscript/values.go +++ b/functions/assemblyscript/values.go @@ -146,32 +146,92 @@ func DecodeValueAs[T any](ctx context.Context, mod wasm.Module, typ plugins.Type case T: return v, nil case map[string]any: - - var out T - err := mapstructure.Decode(v, &out) - if err != nil { - return result, err - } - return out, nil + return mapToStruct[T](v) case []kvp: - switch any(result).(type) { - case []kvp: - return any(v).(T), nil - } + return kvpsToMap[T](v) + } - // convert to map type specified by T - mapType := reflect.TypeOf(result) - if mapType.Kind() != reflect.Map { - return result, fmt.Errorf("unexpected type %T, expected a map type", result) - } - m := reflect.MakeMapWithSize(mapType, len(v)) - for _, kv := range v { - m.SetMapIndex(reflect.ValueOf(kv.Key), reflect.ValueOf(kv.Value)) + return result, fmt.Errorf("unexpected type %T, expected %T", r, result) +} + +var kvpsType = reflect.TypeOf([]kvp{}) + +func mapToStructDecodeHook(f reflect.Type, t reflect.Type, data any) (any, error) { + if t.Kind() == reflect.Map && f.Kind() == reflect.Slice && f == kvpsType { + // convert from kvp[] to map + val := data.([]kvp) + mapType := reflect.MapOf(t.Key(), t.Elem()) + m := reflect.MakeMapWithSize(mapType, len(val)) + for _, kv := range val { + rk := reflect.ValueOf(kv.Key) + rv := reflect.ValueOf(kv.Value) + if rv.Kind() == reflect.Map && t.Elem().Kind() == reflect.Struct { + ps, err := mapToStructReflected(kv.Value.(map[string]any), t.Elem()) + if err != nil { + return nil, err + } + // s is a wrapped pointer to the struct value + rv = reflect.ValueOf(ps).Elem() + } + + m.SetMapIndex(rk, rv) } - return m.Interface().(T), nil + return m.Interface(), nil } + return data, nil +} - return result, fmt.Errorf("unexpected type %T, expected %T", r, result) +func mapToStructReflected(m map[string]any, t reflect.Type) (any, error) { + + result := reflect.New(t).Interface() + config := &mapstructure.DecoderConfig{ + Result: &result, + DecodeHook: mapToStructDecodeHook, + } + + decoder, err := mapstructure.NewDecoder(config) + if err != nil { + return result, err + } + + err = decoder.Decode(m) + return result, err +} + +func mapToStruct[T any](m map[string]any) (T, error) { + var result T + + config := &mapstructure.DecoderConfig{ + Result: &result, + DecodeHook: mapToStructDecodeHook, + } + + decoder, err := mapstructure.NewDecoder(config) + if err != nil { + return result, err + } + + err = decoder.Decode(m) + return result, err +} + +func kvpsToMap[T any](v []kvp) (T, error) { + var result T + switch any(result).(type) { + case []kvp: + return any(v).(T), nil + } + + // convert to map type specified by T + mapType := reflect.TypeOf(result) + if mapType.Kind() != reflect.Map { + return result, fmt.Errorf("unexpected type %T, expected a map type", result) + } + m := reflect.MakeMapWithSize(mapType, len(v)) + for _, kv := range v { + m.SetMapIndex(reflect.ValueOf(kv.Key), reflect.ValueOf(kv.Value)) + } + return m.Interface().(T), nil } func DecodeValue(ctx context.Context, mod wasm.Module, typ plugins.TypeInfo, val uint64) (data any, err error) { From ade037fe9baf50909962b58e430b12ded9841a67 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Mon, 13 May 2024 09:56:58 -0700 Subject: [PATCH 2/2] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97615abc..0a240328 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ - Sentry is no longer used when `HYPERMODE_DEBUG` is enabled [#187](https://github.com/gohypermode/runtime/pull/187) - Only listen on `localhost` when `HYPERMODE_DEBUG` is enabled, to prevent firewall prompt [#188](https://github.com/gohypermode/runtime/pull/188) -- Improve support for marshaling classes [#189](https://github.com/gohypermode/runtime/pull/189) +- Improve support for marshaling classes [#189](https://github.com/gohypermode/runtime/pull/189) [#191](https://github.com/gohypermode/runtime/pull/191) - Add support for binary data fields [#190](https://github.com/gohypermode/runtime/pull/190) ## 2024-05-08 - Version 0.6.6