From 2af0544f8e032bc7a363e897fbd9c6ed4884a1d6 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Mon, 19 Sep 2016 14:31:57 -0700 Subject: [PATCH] Fix panic with embedded struct. GitHub #28 --- decoder.go | 27 +++++++++++++++++++-------- reader_test.go | 18 ++++++++++++++++++ 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/decoder.go b/decoder.go index df3030c..53ab390 100644 --- a/decoder.go +++ b/decoder.go @@ -139,19 +139,30 @@ func (d *decoder) unmarshalBool(size uint, offset uint, result reflect.Value) (u return newOffset, newUnmarshalTypeError(value, result.Type()) } -// follow pointers and create values as necessary +// indirect follows pointers and create values as necessary. This is +// heavily based on encoding/json as my original version had a subtle +// bug. This method should be considered to be licensed under +// https://golang.org/LICENSE func (d *decoder) indirect(result reflect.Value) reflect.Value { for { - if result.Kind() == reflect.Ptr { - if result.IsNil() { - result.Set(reflect.New(result.Type().Elem())) + // Load value from interface, but only if the result will be + // usefully addressable. + if result.Kind() == reflect.Interface && !result.IsNil() { + e := result.Elem() + if e.Kind() == reflect.Ptr && !e.IsNil() { + result = e + continue } - result = result.Elem() - } else if result.Kind() == reflect.Interface && !result.IsNil() { - result = result.Elem() - } else { + } + + if result.Kind() != reflect.Ptr { break } + + if result.IsNil() { + result.Set(reflect.New(result.Type().Elem())) + } + result = result.Elem() } return result } diff --git a/reader_test.go b/reader_test.go index bd2fb64..5d3a0b0 100644 --- a/reader_test.go +++ b/reader_test.go @@ -173,6 +173,24 @@ func TestNonEmptyNilInterface(t *testing.T) { assert.Equal(t, err.Error(), "maxminddb: cannot unmarshal map into type maxminddb.TestInterface") } +type CityTraits struct { + AutonomousSystemNumber uint `json:"autonomous_system_number,omitempty" maxminddb:"autonomous_system_number"` +} + +type City struct { + Traits CityTraits `maxminddb:"traits"` +} + +func TestEmbeddedStructAsInterface(t *testing.T) { + var city City + var result interface{} = city.Traits + + db, err := Open("test-data/test-data/GeoIP2-ISP-Test.mmdb") + require.Nil(t, err) + + assert.Nil(t, db.Lookup(net.ParseIP("1.128.0.0"), &result)) +} + type BoolInterface interface { true() bool }