Skip to content

Commit

Permalink
Add a value hook func
Browse files Browse the repository at this point in the history
This commit adds a third hook func type which has access to the
reflect.Value representations of both the `from` and the `to`
objects. This gives all the same power of the existing hook functions
(types, kinds, and data can all be retrieved from values), but
with the additional benefit of being able to modify the `to` object.

This change is largely backwards compatible. The only change of the
public API (other than adding a new hook type) is a change in the
signature of `DecodeHookExec`.
  • Loading branch information
camdencheek committed Mar 28, 2020
1 parent ba04b21 commit decea91
Show file tree
Hide file tree
Showing 3 changed files with 206 additions and 91 deletions.
47 changes: 31 additions & 16 deletions decode_hooks.go
Expand Up @@ -16,10 +16,11 @@ func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc {
// Create variables here so we can reference them with the reflect pkg
var f1 DecodeHookFuncType
var f2 DecodeHookFuncKind
var f3 DecodeHookFuncValue

// Fill in the variables into this interface and the rest is done
// automatically using the reflect package.
potential := []interface{}{f1, f2}
potential := []interface{}{f1, f2, f3}

v := reflect.ValueOf(h)
vt := v.Type()
Expand All @@ -38,13 +39,15 @@ func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc {
// that took reflect.Kind instead of reflect.Type.
func DecodeHookExec(
raw DecodeHookFunc,
from reflect.Type, to reflect.Type,
data interface{}) (interface{}, error) {
from reflect.Value, to reflect.Value) (interface{}, error) {

switch f := typedDecodeHook(raw).(type) {
case DecodeHookFuncType:
return f(from, to, data)
return f(from.Type(), to.Type(), from.Interface())
case DecodeHookFuncKind:
return f(from.Kind(), to.Kind(), data)
return f(from.Kind(), to.Kind(), from.Interface())
case DecodeHookFuncValue:
return f(from, to)
default:
return nil, errors.New("invalid decode hook signature")
}
Expand All @@ -56,22 +59,16 @@ func DecodeHookExec(
// The composed funcs are called in order, with the result of the
// previous transformation.
func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc {
return func(
f reflect.Type,
t reflect.Type,
data interface{}) (interface{}, error) {
return func(f reflect.Value, t reflect.Value) (interface{}, error) {
var err error
var data interface{}
newFrom := f
for _, f1 := range fs {
data, err = DecodeHookExec(f1, f, t, data)
data, err = DecodeHookExec(f1, newFrom, t)
if err != nil {
return nil, err
}

// Modify the from kind to be correct with the new data
f = nil
if val := reflect.ValueOf(data); val.IsValid() {
f = val.Type()
}
newFrom = reflect.ValueOf(data)
}

return data, nil
Expand Down Expand Up @@ -215,3 +212,21 @@ func WeaklyTypedHook(

return data, nil
}

func RecursiveStructToMapHookFunc() DecodeHookFunc {
return func(f reflect.Value, t reflect.Value) (interface{}, error) {
if f.Kind() != reflect.Struct {
return f.Interface(), nil
}

var i interface{} = struct{}{}
if t.Type() != reflect.TypeOf(&i).Elem() {
return f.Interface(), nil
}

m := make(map[string]interface{})
t.Set(reflect.ValueOf(m))

return f.Interface(), nil
}
}

0 comments on commit decea91

Please sign in to comment.