Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More improvements to marshaling classes #191

Merged
merged 2 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
102 changes: 81 additions & 21 deletions functions/assemblyscript/values.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down