diff --git a/targets.go b/targets.go index b0f9db2..17e45f2 100644 --- a/targets.go +++ b/targets.go @@ -58,6 +58,35 @@ func (t valueTarget) setFloat64(v float64) error { return nil } +// interfaceTarget wraps an other target to dereference on get. +type interfaceTarget struct { + x target +} + +func (t interfaceTarget) get() reflect.Value { + return t.x.get().Elem() +} + +func (t interfaceTarget) set(v reflect.Value) error { + return t.x.set(v) +} + +func (t interfaceTarget) setString(v string) error { + return t.x.setString(v) +} + +func (t interfaceTarget) setBool(v bool) error { + return t.x.setBool(v) +} + +func (t interfaceTarget) setInt64(v int64) error { + return t.x.setInt64(v) +} + +func (t interfaceTarget) setFloat64(v float64) error { + return t.x.setFloat64(v) +} + // mapTarget targets a specific key of a map. type mapTarget struct { v reflect.Value @@ -199,66 +228,62 @@ func pushNew(t target) (target, error) { func scopeTableTarget(append bool, t target, name string) (target, error) { x := t.get() - if x.Kind() == reflect.Interface { - t, err := initInterface(append, t) + switch x.Kind() { + case reflect.Interface: + t, err := scopeInterface(append, t) if err != nil { return t, err } - x = t.get() + return scopeTableTarget(append, t, name) + case reflect.Struct: + return scopeStruct(x, name) + case reflect.Map: + return scopeMap(x, name) + case reflect.Slice: + return scopeSlice(append, t) + default: + panic(fmt.Errorf("can't scope on a %s", x.Kind())) } + return t, nil +} - if x.Kind() == reflect.Slice { - return scopeSlice(t, append) - } - - t, err := scope(x, name) +func scopeInterface(append bool, t target) (target, error) { + err := initInterface(append, t) if err != nil { return t, err } - return t, nil + return interfaceTarget{t}, nil } // initInterface makes sure that the interface pointed at by the target is not // nil. // Returns the target to the initialized value of the target. -func initInterface(append bool, t target) (target, error) { +func initInterface(append bool, t target) error { x := t.get() if x.Kind() != reflect.Interface { panic("this should only be called on interfaces") } - if x.IsNil() { - var newElement reflect.Value - if append { - newElement = reflect.MakeSlice(reflect.TypeOf([]interface{}{}), 0, 0) - } else { - newElement = reflect.MakeMap(reflect.TypeOf(map[string]interface{}{})) - } - err := t.set(newElement) - if err != nil { - return t, err - } - x = t.get() + if !x.IsNil() { + return nil } - x = x.Elem() - t = valueTarget(x) - return t, nil -} - -func scope(v reflect.Value, name string) (target, error) { - switch v.Kind() { - case reflect.Struct: - return scopeStruct(v, name) - case reflect.Map: - return scopeMap(v, name) - default: - panic(fmt.Errorf("can't scope on a %s", v.Kind())) + var newElement reflect.Value + if append { + newElement = reflect.MakeSlice(reflect.TypeOf([]interface{}{}), 0, 0) + } else { + newElement = reflect.MakeMap(reflect.TypeOf(map[string]interface{}{})) } + err := t.set(newElement) + if err != nil { + return err + } + + return nil } -func scopeSlice(t target, append bool) (target, error) { +func scopeSlice(append bool, t target) (target, error) { v := t.get() if append { diff --git a/targets_test.go b/targets_test.go index 1522d22..2fd5708 100644 --- a/targets_test.go +++ b/targets_test.go @@ -39,7 +39,7 @@ func TestStructTarget_Ensure(t *testing.T) { for _, e := range examples { t.Run(e.desc, func(t *testing.T) { - target, err := scope(e.input, e.name) + target, err := scopeTableTarget(false, valueTarget(e.input), e.name) require.NoError(t, err) err = ensureSlice(target) v := target.get() @@ -86,7 +86,7 @@ func TestStructTarget_SetString(t *testing.T) { for _, e := range examples { t.Run(e.desc, func(t *testing.T) { - target, err := scope(e.input, e.name) + target, err := scopeTableTarget(false, valueTarget(e.input), e.name) require.NoError(t, err) err = setString(target, str) v := target.get() @@ -102,7 +102,7 @@ func TestPushNew(t *testing.T) { } d := Doc{} - x, err := scope(reflect.ValueOf(&d).Elem(), "A") + x, err := scopeTableTarget(false, valueTarget(reflect.ValueOf(&d).Elem()), "A") require.NoError(t, err) n, err := pushNew(x) @@ -122,7 +122,7 @@ func TestPushNew(t *testing.T) { } d := Doc{} - x, err := scope(reflect.ValueOf(&d).Elem(), "A") + x, err := scopeTableTarget(false, valueTarget(reflect.ValueOf(&d).Elem()), "A") require.NoError(t, err) n, err := pushNew(x) @@ -161,7 +161,7 @@ func TestScope_Struct(t *testing.T) { for _, e := range examples { t.Run(e.desc, func(t *testing.T) { - x, err := scope(e.input, e.name) + x, err := scopeTableTarget(false, valueTarget(e.input), e.name) if e.err { require.Error(t, err) } else { diff --git a/unmarshaler.go b/unmarshaler.go index 478a127..ba94472 100644 --- a/unmarshaler.go +++ b/unmarshaler.go @@ -95,7 +95,7 @@ func scopeWithArrayTable(x target, key []ast.Node) (target, error) { v := x.get() if v.Kind() == reflect.Interface { - x, err = initInterface(true, x) + x, err = scopeInterface(true, x) if err != nil { return x, err } @@ -103,7 +103,7 @@ func scopeWithArrayTable(x target, key []ast.Node) (target, error) { } if v.Kind() == reflect.Slice { - return scopeSlice(x, true) + return scopeSlice(true, x) } return x, err diff --git a/unmarshaler_test.go b/unmarshaler_test.go index e748d0c..b061147 100644 --- a/unmarshaler_test.go +++ b/unmarshaler_test.go @@ -439,12 +439,12 @@ B = "data"`, "Products": []interface{}{ map[string]interface{}{ "Name": "Hammer", - "Sku": 738594937, + "Sku": int64(738594937), }, nil, map[string]interface{}{ "Name": "Nail", - "Sku": 284758393, + "Sku": int64(284758393), "Color": "gray", }, },