Skip to content

Commit 344388f

Browse files
committed
fix(GetForToken): added support for pointer to interface{}
reflect-based switch doesn't work when the value passed is *any: the resulting indirection is typed as reflect.Interface and not the actual underlying type. * added more stringent checking on nil values (i.e. covers interface{}(nil) * contributes: go-swagger/go-swagger#1898 (pointers to content of a x-... swagger extension) Signed-off-by: Frederic BIDON <[email protected]>
1 parent 6cf0fb8 commit 344388f

File tree

2 files changed

+70
-13
lines changed

2 files changed

+70
-13
lines changed

pointer.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,16 +110,36 @@ func SetForToken(document any, decodedToken string, value any) (any, error) {
110110
return document, setSingleImpl(document, value, decodedToken, swag.DefaultJSONNameProvider)
111111
}
112112

113+
func isNil(input any) bool {
114+
if input == nil {
115+
return true
116+
}
117+
118+
kind := reflect.TypeOf(input).Kind()
119+
switch kind { //nolint:exhaustive
120+
case reflect.Ptr, reflect.Map, reflect.Slice, reflect.Chan:
121+
return reflect.ValueOf(input).IsNil()
122+
default:
123+
return false
124+
}
125+
}
126+
113127
func getSingleImpl(node any, decodedToken string, nameProvider *swag.NameProvider) (any, reflect.Kind, error) {
114128
rValue := reflect.Indirect(reflect.ValueOf(node))
115129
kind := rValue.Kind()
130+
if isNil(node) {
131+
return nil, kind, fmt.Errorf("nil value has not field %q", decodedToken)
132+
}
116133

117-
if rValue.Type().Implements(jsonPointableType) {
118-
r, err := node.(JSONPointable).JSONLookup(decodedToken)
134+
switch typed := node.(type) {
135+
case JSONPointable:
136+
r, err := typed.JSONLookup(decodedToken)
119137
if err != nil {
120138
return nil, kind, err
121139
}
122140
return r, kind, nil
141+
case *any: // case of a pointer to interface, that is not resolved by reflect.Indirect
142+
return getSingleImpl(*typed, decodedToken, nameProvider)
123143
}
124144

125145
switch kind { //nolint:exhaustive

pointer_test.go

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -140,19 +140,56 @@ func TestIsEmpty(t *testing.T) {
140140
func TestGetSingle(t *testing.T) {
141141
const in = `/obj`
142142

143-
_, err := New(in)
144-
require.NoError(t, err)
145-
result, _, err := GetForToken(testDocumentJSON, "obj")
146-
require.NoError(t, err)
147-
assert.Len(t, result, TestNodeObjNBItems)
143+
t.Run("should create a new JSON pointer", func(t *testing.T) {
144+
_, err := New(in)
145+
require.NoError(t, err)
146+
})
148147

149-
result, _, err = GetForToken(testStructJSONDoc, "Obj")
150-
require.Error(t, err)
151-
assert.Nil(t, result)
148+
t.Run(`should find token "obj" in JSON`, func(t *testing.T) {
149+
result, _, err := GetForToken(testDocumentJSON, "obj")
150+
require.NoError(t, err)
151+
assert.Len(t, result, TestNodeObjNBItems)
152+
})
152153

153-
result, _, err = GetForToken(testStructJSONDoc, "Obj2")
154-
require.Error(t, err)
155-
assert.Nil(t, result)
154+
t.Run(`should find token "obj" in type alias interface`, func(t *testing.T) {
155+
type alias interface{}
156+
var in alias = testDocumentJSON
157+
result, _, err := GetForToken(in, "obj")
158+
require.NoError(t, err)
159+
assert.Len(t, result, TestNodeObjNBItems)
160+
})
161+
162+
t.Run(`should find token "obj" in pointer to interface`, func(t *testing.T) {
163+
in := &testDocumentJSON
164+
result, _, err := GetForToken(in, "obj")
165+
require.NoError(t, err)
166+
assert.Len(t, result, TestNodeObjNBItems)
167+
})
168+
169+
t.Run(`should not find token "Obj" in struct`, func(t *testing.T) {
170+
result, _, err := GetForToken(testStructJSONDoc, "Obj")
171+
require.Error(t, err)
172+
assert.Nil(t, result)
173+
})
174+
175+
t.Run(`should not find token "Obj2" in struct`, func(t *testing.T) {
176+
result, _, err := GetForToken(testStructJSONDoc, "Obj2")
177+
require.Error(t, err)
178+
assert.Nil(t, result)
179+
})
180+
181+
t.Run(`should not find token in nil`, func(t *testing.T) {
182+
result, _, err := GetForToken(nil, "obj")
183+
require.Error(t, err)
184+
assert.Nil(t, result)
185+
})
186+
187+
t.Run(`should not find token in nil interface`, func(t *testing.T) {
188+
var in interface{}
189+
result, _, err := GetForToken(in, "obj")
190+
require.Error(t, err)
191+
assert.Nil(t, result)
192+
})
156193
}
157194

158195
type pointableImpl struct {

0 commit comments

Comments
 (0)