add support for anonymous struct fields these fields are encoded as if their inner exported fields were fields on the outer struct, the same as encoding/json
diff --git a/query/encode.go b/query/encode.go index 587745c..e94c8f7 100644 --- a/query/encode.go +++ b/query/encode.go
@@ -13,13 +13,21 @@ // Values returns the url.Values encoding of v. func Values(v interface{}) (url.Values, error) { - values := url.Values{} + values := &url.Values{} val := reflect.ValueOf(v) if val.Kind() != reflect.Struct { return nil, fmt.Errorf("query: Values() expects struct input") } + reflectValue(values, val) + + return *values, nil +} + +func reflectValue(values *url.Values, val reflect.Value) { + embedded := []reflect.Value{} + typ := val.Type() for i := 0; i < typ.NumField(); i++ { sf := typ.Field(i) @@ -27,16 +35,22 @@ continue } + sv := val.Field(i) tag := sf.Tag.Get("url") if tag == "-" { continue } name, opts := parseTag(tag) if name == "" { + if sf.Anonymous && sv.Kind() == reflect.Struct { + // save embedded struct for later processing + embedded = append(embedded, sv) + continue + } + name = sf.Name } - sv := val.Field(i) if opts.Contains("omitempty") && isEmptyValue(sv) { continue } @@ -99,7 +113,9 @@ } } - return values, nil + for _, f := range embedded { + reflectValue(values, f) + } } func isEmptyValue(v reflect.Value) bool {
diff --git a/query/encode_test.go b/query/encode_test.go index 78c35d6..ce6c3f1 100644 --- a/query/encode_test.go +++ b/query/encode_test.go
@@ -123,6 +123,46 @@ } } +type A struct { + B +} + +type B struct { + C string +} + +type D struct { + B + C string +} + +func TestValues_embeddedStructs(t *testing.T) { + tests := []struct { + in interface{} + want url.Values + }{ + { + A{B{C: "foo"}}, + url.Values{"C": {"foo"}}, + }, + { + D{B: B{C: "bar"}, C: "foo"}, + url.Values{"C": {"foo", "bar"}}, + }, + } + + for i, tt := range tests { + v, err := Values(tt.in) + if err != nil { + t.Errorf("%d. Values(%q) returned error: %v", i, tt.in, err) + } + + if !reflect.DeepEqual(tt.want, v) { + t.Errorf("%d. Values(%q) returned %v, want %v", i, tt.in, v, tt.want) + } + } +} + func TestValues_invalidInput(t *testing.T) { _, err := Values("") if err == nil {