Skip to content
Merged
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
49 changes: 34 additions & 15 deletions cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,50 @@ func getStructCache(rt reflect.Type) *structInfo {

// Build struct info
info := &structInfo{name: rt.Name()}

// struct fields traversal
for i := 0; i < rt.NumField(); i++ {
field := rt.Field(i)

tag := field.Tag.Get("qp")

fieldType := field.Type
if fieldType.Kind() == reflect.Ptr {
fieldType = fieldType.Elem()
}

isStruct := fieldType.Kind() == reflect.Struct && fieldType != reflect.TypeFor[time.Time]()

// traverse embedded structs
if field.Anonymous && isStruct {
info.fields = append(info.fields, fieldInfo{
name: field.Name,
typ: field.Type,
index: field.Index,
isNested: true,
})
continue
}

if !field.IsExported() {
if tag != "" {
info.hasUnexportedWithTag = true
}
continue
}

// tag on struct will be ignored
if isStruct {
info.fields = append(info.fields, fieldInfo{
name: field.Name,
typ: field.Type,
index: field.Index,
isNested: true,
})
continue
}

// leaf field
if tag != "" {
info.fields = append(info.fields, fieldInfo{
name: field.Name,
Expand All @@ -49,22 +82,8 @@ func getStructCache(rt reflect.Type) *structInfo {
index: field.Index,
isNested: false,
})
} else {
// Check if this field is a nested struct (struct or pointer to struct)
fieldType := field.Type
if fieldType.Kind() == reflect.Ptr {
fieldType = fieldType.Elem()
}
if fieldType.Kind() == reflect.Struct && fieldType != reflect.TypeOf(time.Time{}) {
info.fields = append(info.fields, fieldInfo{
name: field.Name,
tag: "",
typ: field.Type, // Keep the original type (may be pointer)
index: field.Index,
isNested: true,
})
}
}

}

// LoadOrStore handles race conditions atomically
Expand Down
44 changes: 44 additions & 0 deletions parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,50 @@ func TestNestedStruct(t *testing.T) {
err = Parse(values, &s)
assert.Error(t, err)
})

t.Run("Embedded/Anonymous-Nested-Struct", func(t *testing.T) {
type filter1 struct {
Foo []string `qp:"foo"`
}

type filter2 struct {
filter1
Bar []string `qp:"bar"`
}

type sortOrder struct {
By string `qp:"sort_by"`
Dir string `qp:"sort_dir"`
}

type input struct {
Filter filter2
Sort sortOrder
}

var in input
queryParams := "foo=f1&bar=b1,b2&sort_by=foo&sort_dir=desc"
expected := input{
Filter: filter2{
filter1: filter1{
Foo: []string{"f1"},
},
Bar: []string{"b1", "b2"},
},
Sort: sortOrder{
By: "foo",
Dir: "desc",
},
}

values, err := url.ParseQuery(queryParams)
require.NoError(t, err)

err = Parse(values, &in)
require.NoError(t, err)

assert.Equal(t, in, expected)
})
}

func ptr[T any](v T) *T {
Expand Down
Loading