blob: e557793478e42b0f223d5f3e94852b8d5c740fed [file]
package query
import (
"fmt"
"net/url"
"reflect"
"time"
)
var timeType = reflect.TypeOf(time.Time{})
// Values returns the url.Values encoding of v.
func Values(v interface{}) (url.Values, error) {
values := url.Values{}
val := reflect.ValueOf(v)
if val.Kind() != reflect.Struct {
return nil, fmt.Errorf("query: Values() expects struct input")
}
typ := val.Type()
for i := 0; i < typ.NumField(); i++ {
sf := typ.Field(i)
if sf.PkgPath != "" { // unexported
continue
}
tag := sf.Tag.Get("url")
if tag == "-" {
continue
}
name, opts := parseTag(tag)
if name == "" {
name = sf.Name
}
sv := val.Field(i)
if opts.Contains("omitempty") && isEmptyValue(sv) {
continue
}
if !isEmptyValue(sv) && sv.Kind() == reflect.Ptr {
sv = sv.Elem()
}
switch sv.Kind() {
case reflect.Slice, reflect.Array:
for i := 0; i < sv.Len(); i++ {
values.Add(name, fmt.Sprint(sv.Index(i)))
}
default:
switch sv.Type() {
case timeType:
t := sv.Interface().(time.Time)
if opts.Contains("unix") {
values.Add(name, fmt.Sprint(t.Unix()))
} else {
values.Add(name, t.Format(time.RFC3339))
}
default:
values.Add(name, fmt.Sprint(sv.Interface()))
}
}
}
return values, nil
}
func isEmptyValue(v reflect.Value) bool {
switch v.Kind() {
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
return v.Len() == 0
case reflect.Bool:
return !v.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint() == 0
case reflect.Float32, reflect.Float64:
return v.Float() == 0
case reflect.Interface, reflect.Ptr:
return v.IsNil()
}
switch v.Type() {
case timeType:
return v.Interface().(time.Time).IsZero()
}
return false
}