in internal/apiquery/encoder.go [126:215]
func (e *encoder) newStructTypeEncoder(t reflect.Type) encoderFunc {
if t.Implements(reflect.TypeOf((*internalparam.FieldLike)(nil)).Elem()) {
return e.newFieldTypeEncoder(t)
}
if t.Implements(reflect.TypeOf((*param.Optional)(nil)).Elem()) {
return e.newRichFieldTypeEncoder(t)
}
for i := 0; i < t.NumField(); i++ {
if t.Field(i).Type == paramUnionType && t.Field(i).Anonymous {
return e.newStructUnionTypeEncoder(t)
}
}
encoderFields := []encoderField{}
// This helper allows us to recursively collect field encoders into a flat
// array. The parameter `index` keeps track of the access patterns necessary
// to get to some field.
var collectEncoderFields func(r reflect.Type, index []int)
collectEncoderFields = func(r reflect.Type, index []int) {
for i := 0; i < r.NumField(); i++ {
idx := append(index, i)
field := t.FieldByIndex(idx)
if !field.IsExported() {
continue
}
// If this is an embedded struct, traverse one level deeper to extract
// the field and get their encoders as well.
if field.Anonymous {
collectEncoderFields(field.Type, idx)
continue
}
// If query tag is not present, then we skip, which is intentionally
// different behavior from the stdlib.
ptag, ok := parseQueryStructTag(field)
if !ok {
continue
}
if (ptag.name == "-" || ptag.name == "") && !ptag.inline {
continue
}
dateFormat, ok := parseFormatStructTag(field)
oldFormat := e.dateFormat
if ok {
switch dateFormat {
case "date-time":
e.dateFormat = time.RFC3339
case "date":
e.dateFormat = "2006-01-02"
}
}
var encoderFn encoderFunc
if ptag.omitzero {
typeEncoderFn := e.typeEncoder(field.Type)
encoderFn = func(key string, value reflect.Value) ([]Pair, error) {
if value.IsZero() {
return nil, nil
}
return typeEncoderFn(key, value)
}
} else {
encoderFn = e.typeEncoder(field.Type)
}
encoderFields = append(encoderFields, encoderField{ptag, encoderFn, idx})
e.dateFormat = oldFormat
}
}
collectEncoderFields(t, []int{})
return func(key string, value reflect.Value) (pairs []Pair, err error) {
for _, ef := range encoderFields {
var subkey string = e.renderKeyPath(key, ef.tag.name)
if ef.tag.inline {
subkey = key
}
field := value.FieldByIndex(ef.idx)
subpairs, suberr := ef.fn(subkey, field)
if suberr != nil {
err = suberr
}
pairs = append(pairs, subpairs...)
}
return
}
}