package tracker import ( "errors" "net/url" "reflect" "strconv" "strings" ) func MarshalQuery(v interface{}) (string, error) { val := reflect.ValueOf(v) return marshalQuery(val) } func parseTag(tag string) (string, string) { if i := strings.Index(tag, ","); i != -1 { return tag[:i], tag[i+1:] } return tag, "" } func isZero(v reflect.Value) bool { switch v.Kind() { case reflect.String: return v.String() == "" case reflect.Int: return v.Int() == 0 } return false } func marshalQuery(v reflect.Value) (string, error) { if v.Kind() != reflect.Struct { return "", errors.New("must be a struct") } q := url.Values{} t := v.Type() for i := 0; i < t.NumField(); i++ { name := strings.ToLower(t.Field(i).Name) tag, param := parseTag(t.Field(i).Tag.Get("query")) if tag != "" { name = tag } f := v.Field(i) if param == "optional" && isZero(f) { continue } switch f.Kind() { case reflect.Int: q.Add(name, strconv.Itoa(int(f.Int()))) case reflect.String: q.Add(name, f.String()) case reflect.Slice: if f.Type().Elem().Kind() == reflect.Uint8 { q.Add(name, string(f.Bytes())) } } } return "?" + q.Encode(), nil }