Repository: mitchellh/mapstructure (go)

Issue:
Type error when using DecodeHook to decode into interface
**What version of are you using (git log -1 --format=oneline)?**
fe40af7a9c397fa3ddba203c38a5042c5d0475ad (HEAD -> master, tag: v1.1.1, origin/master, origin/HEAD) Update CHANGELOG

**What did you do?**
Run attached test:
```go
package mapstructure_test

import (
	"reflect"
	"testing"

	"github.com/mitchellh/mapstructure"
)

type DecodeInto struct {
	Test Interface
}

type Interface interface {
	DoStuff()
}

type ImplementsInterface struct {
	Name string
}

func (i *ImplementsInterface) DoStuff() {}

func TestInterfaceDecodeHook(t *testing.T) {
	in := map[string]string{"test": "test"}
	decodeInto := new(DecodeInto)

	config := &mapstructure.DecoderConfig{
		DecodeHook: stringToInterfaceDecodeHook,
		Result:     decodeInto,
	}
	decoder, _ := mapstructure.NewDecoder(config)
	err := decoder.Decode(in)
	if err != nil {
		t.Errorf("Decode returned error: %s", err)
	}
}

func stringToInterfaceDecodeHook(from, to reflect.Type, data interface{}) (interface{}, error) {
	if from.Kind() != reflect.String {
		return data, nil
	}

	if to != reflect.TypeOf((*Interface)(nil)).Elem() {
		return data, nil
	}
	// Ensure interface is satisfied
	var impl Interface = &ImplementsInterface{data.(string)}
	return impl, nil
}
```

**What did you expect to see?**
I expected the test to pass.

**What did you see instead?**
```
Running tool: /usr/local/opt/go@1.11/bin/go test -timeout 30s github.com/mitchellh/mapstructure -run ^TestInterfaceDecodeHook$

--- FAIL: TestInterfaceDecodeHook (0.00s)
    /Users/sebastianwidmer/go/src/github.com/mitchellh/mapstructure/mapstructure_interface_test.go:35: Decode returned error: 1 error(s) decoding:
        
        * 'Test' expected type 'mapstructure_test.Interface', got 'mapstructure_test.ImplementsInterface'
FAIL
FAIL	github.com/mitchellh/mapstructure	0.009s
Error: Tests failed.
```

This happens since upgrading to v1.1.1 to all our hooks decoding into interfaces. Will dig deeper into the changes tomorrow and try to create a pull request.


Pull request:
Fix #140 Type error when using DecodeHook to decode into interface
See the two commits 141d431 and 5ba0c72 as possible fixes described in https://github.com/mitchellh/mapstructure/issues/140#issuecomment-426651303.

Patch:
diff --git a/mapstructure.go b/mapstructure.go
index 95eb686..f20c4df 100644
--- a/mapstructure.go
+++ b/mapstructure.go
@@ -316,7 +316,12 @@ func (d *Decoder) decodeBasic(name string, data interface{}, val reflect.Value)
 	if val.IsValid() && val.Elem().IsValid() {
 		return d.decode(name, data, val.Elem())
 	}
-	dataVal := reflect.Indirect(reflect.ValueOf(data))
+
+	dataVal := reflect.ValueOf(data)
+	// Only dereference to `interface{}` not programmer defined interfaces
+	if val.Type() == reflect.TypeOf((*interface{})(nil)).Elem() {
+		dataVal = reflect.Indirect(dataVal)
+	}
 	if !dataVal.IsValid() {
 		dataVal = reflect.Zero(val.Type())
 	}


Test patch:
diff --git a/mapstructure_bugs_test.go b/mapstructure_bugs_test.go
index 1319da5..ba6590a 100644
--- a/mapstructure_bugs_test.go
+++ b/mapstructure_bugs_test.go
@@ -370,3 +370,91 @@ func TestNextSquashMapstructure(t *testing.T) {
 		t.Fatal("value should be baz")
 	}
 }
+
+type ImplementsInterfacePointerReceiver struct {
+	Name string
+}
+
+func (i *ImplementsInterfacePointerReceiver) DoStuff() {}
+
+type ImplementsInterfaceValueReceiver string
+
+func (i ImplementsInterfaceValueReceiver) DoStuff() {}
+
+// GH-140 Type error when using DecodeHook to decode into interface
+func TestDecode_DecodeHookInterface(t *testing.T) {
+	t.Parallel()
+
+	type Interface interface {
+		DoStuff()
+	}
+	type DecodeIntoInterface struct {
+		Test Interface
+	}
+
+	testData := map[string]string{"test": "test"}
+
+	stringToPointerInterfaceDecodeHook := func(from, to reflect.Type, data interface{}) (interface{}, error) {
+		if from.Kind() != reflect.String {
+			return data, nil
+		}
+
+		if to != reflect.TypeOf((*Interface)(nil)).Elem() {
+			return data, nil
+		}
+		// Ensure interface is satisfied
+		var impl Interface = &ImplementsInterfacePointerReceiver{data.(string)}
+		return impl, nil
+	}
+
+	stringToValueInterfaceDecodeHook := func(from, to reflect.Type, data interface{}) (interface{}, error) {
+		if from.Kind() != reflect.String {
+			return data, nil
+		}
+
+		if to != reflect.TypeOf((*Interface)(nil)).Elem() {
+			return data, nil
+		}
+		// Ensure interface is satisfied
+		var impl Interface = ImplementsInterfaceValueReceiver(data.(string))
+		return impl, nil
+	}
+
+	{
+		decodeInto := new(DecodeIntoInterface)
+
+		decoder, _ := NewDecoder(&DecoderConfig{
+			DecodeHook: stringToPointerInterfaceDecodeHook,
+			Result:     decodeInto,
+		})
+
+		err := decoder.Decode(testData)
+		if err != nil {
+			t.Fatalf("Decode returned error: %s", err)
+		}
+
+		expected := &ImplementsInterfacePointerReceiver{"test"}
+		if !reflect.DeepEqual(decodeInto.Test, expected) {
+			t.Fatalf("expected: %#v (%T), got: %#v (%T)", decodeInto.Test, decodeInto.Test, expected, expected)
+		}
+	}
+
+	{
+		decodeInto := new(DecodeIntoInterface)
+
+		decoder, _ := NewDecoder(&DecoderConfig{
+			DecodeHook: stringToValueInterfaceDecodeHook,
+			Result:     decodeInto,
+		})
+
+		err := decoder.Decode(testData)
+		if err != nil {
+			t.Fatalf("Decode returned error: %s", err)
+		}
+
+		expected := ImplementsInterfaceValueReceiver("test")
+		if !reflect.DeepEqual(decodeInto.Test, expected) {
+			t.Fatalf("expected: %#v (%T), got: %#v (%T)", decodeInto.Test, decodeInto.Test, expected, expected)
+		}
+	}
+}