Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions query/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@ func reflectValue(values url.Values, val reflect.Value, scope string) error {
}

if sv.Type().Implements(encoderType) {
// a nil interface value has no concrete encoder to call; skip the
// field instead of panicking on the type assertion below
if sv.Kind() == reflect.Interface && sv.IsNil() {
continue
}

// if sv is a nil pointer and the custom encoder is defined on a non-pointer
// method receiver, set sv to the zero value of the underlying type
if !reflect.Indirect(sv).IsValid() && sv.Type().Elem().Implements(encoderType) {
Expand Down
33 changes: 33 additions & 0 deletions query/encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,39 @@ func TestValues_CustomEncodingPointer(t *testing.T) {
}
}

// customEncoder is an interface that embeds Encoder.
type customEncoder interface {
Encoder
}

// TestValues_NilEncoderInterface verifies that a nil interface field whose
// type implements Encoder is skipped instead of panicking, and that a non-nil
// value is still encoded normally.
func TestValues_NilEncoderInterface(t *testing.T) {
type st struct {
V customEncoder
}

// nil interface field: nothing to encode, must not panic
v, err := Values(st{})
if err != nil {
t.Fatalf("Values() returned error: %v", err)
}
if len(v) != 0 {
t.Errorf("Values() = %v, want no values for a nil interface field", v)
}

// non-nil interface field still uses the concrete encoder
v, err = Values(st{V: customEncodedStrings{"a"}})
if err != nil {
t.Fatalf("Values() returned error: %v", err)
}
want := url.Values{"V.0": {"a"}}
if diff := cmp.Diff(want, v); diff != "" {
t.Errorf("Values() mismatch:\n%s", diff)
}
}

func TestIsEmptyValue(t *testing.T) {
str := "string"
tests := []struct {
Expand Down
Loading