aboutsummaryrefslogtreecommitdiff
path: root/query/query.go
blob: 3437ee51ce1513e835a966106a8a96c4133d6607 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package query

import (
	"errors"
	"net/url"
	"reflect"
	"strconv"
	"strings"
)

func Marshal(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, reflect.Slice:
		return v.Len() == 0
	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:
		return v.Uint() == 0
	case reflect.Bool:
		return v.Bool() == false
	}
	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.Bool:
			if f.Bool() == true {
				q.Add(name, "1")
			} else {
				q.Add(name, "0")
			}
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			q.Add(name, strconv.Itoa(int(f.Int())))
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
			q.Add(name, strconv.Itoa(int(f.Uint())))
		case reflect.String:
			q.Add(name, f.String())
		case reflect.Slice, reflect.Array:
			if f.Type().Elem().Kind() == reflect.Uint8 {
				tmp := make([]byte, f.Len())
				for i := 0; i < f.Len(); i++ {
					tmp[i] = byte(f.Index(i).Uint())
				}
				q.Add(name, string(tmp))
			}
		}
	}
	return "?" + q.Encode(), nil
}