1
16
17 package queryparams
18
19 import (
20 "fmt"
21 "net/url"
22 "reflect"
23 "strings"
24 )
25
26
27 type Marshaler interface {
28 MarshalQueryParameter() (string, error)
29 }
30
31
32 type Unmarshaler interface {
33 UnmarshalQueryParameter(string) error
34 }
35
36 func jsonTag(field reflect.StructField) (string, bool) {
37 structTag := field.Tag.Get("json")
38 if len(structTag) == 0 {
39 return "", false
40 }
41 parts := strings.Split(structTag, ",")
42 tag := parts[0]
43 if tag == "-" {
44 tag = ""
45 }
46 omitempty := false
47 parts = parts[1:]
48 for _, part := range parts {
49 if part == "omitempty" {
50 omitempty = true
51 break
52 }
53 }
54 return tag, omitempty
55 }
56
57 func isPointerKind(kind reflect.Kind) bool {
58 return kind == reflect.Pointer
59 }
60
61 func isStructKind(kind reflect.Kind) bool {
62 return kind == reflect.Struct
63 }
64
65 func isValueKind(kind reflect.Kind) bool {
66 switch kind {
67 case reflect.String, reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16,
68 reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8,
69 reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32,
70 reflect.Float64, reflect.Complex64, reflect.Complex128:
71 return true
72 default:
73 return false
74 }
75 }
76
77 func zeroValue(value reflect.Value) bool {
78 return reflect.DeepEqual(reflect.Zero(value.Type()).Interface(), value.Interface())
79 }
80
81 func customMarshalValue(value reflect.Value) (reflect.Value, bool) {
82
83 if !value.CanInterface() {
84 return reflect.Value{}, false
85 }
86
87 marshaler, ok := value.Interface().(Marshaler)
88 if !ok {
89 if !isPointerKind(value.Kind()) && value.CanAddr() {
90 marshaler, ok = value.Addr().Interface().(Marshaler)
91 if !ok {
92 return reflect.Value{}, false
93 }
94 } else {
95 return reflect.Value{}, false
96 }
97 }
98
99
100
101 if isPointerKind(value.Kind()) && zeroValue(value) {
102 return reflect.ValueOf(""), true
103 }
104
105
106 v, err := marshaler.MarshalQueryParameter()
107 if err != nil {
108 return reflect.Value{}, false
109 }
110 return reflect.ValueOf(v), true
111 }
112
113 func addParam(values url.Values, tag string, omitempty bool, value reflect.Value) {
114 if omitempty && zeroValue(value) {
115 return
116 }
117 val := ""
118 iValue := fmt.Sprintf("%v", value.Interface())
119
120 if iValue != "<nil>" {
121 val = iValue
122 }
123 values.Add(tag, val)
124 }
125
126 func addListOfParams(values url.Values, tag string, omitempty bool, list reflect.Value) {
127 for i := 0; i < list.Len(); i++ {
128 addParam(values, tag, omitempty, list.Index(i))
129 }
130 }
131
132
133
134
135 func Convert(obj interface{}) (url.Values, error) {
136 result := url.Values{}
137 if obj == nil {
138 return result, nil
139 }
140 var sv reflect.Value
141 switch reflect.TypeOf(obj).Kind() {
142 case reflect.Pointer, reflect.Interface:
143 sv = reflect.ValueOf(obj).Elem()
144 default:
145 return nil, fmt.Errorf("expecting a pointer or interface")
146 }
147 st := sv.Type()
148 if !isStructKind(st.Kind()) {
149 return nil, fmt.Errorf("expecting a pointer to a struct")
150 }
151
152
153 convertStruct(result, st, sv)
154
155 return result, nil
156 }
157
158 func convertStruct(result url.Values, st reflect.Type, sv reflect.Value) {
159 for i := 0; i < st.NumField(); i++ {
160 field := sv.Field(i)
161 tag, omitempty := jsonTag(st.Field(i))
162 if len(tag) == 0 {
163 continue
164 }
165 ft := field.Type()
166
167 kind := ft.Kind()
168 if isPointerKind(kind) {
169 ft = ft.Elem()
170 kind = ft.Kind()
171 if !field.IsNil() {
172 field = reflect.Indirect(field)
173
174
175 omitempty = false
176 }
177 }
178
179 switch {
180 case isValueKind(kind):
181 addParam(result, tag, omitempty, field)
182 case kind == reflect.Array || kind == reflect.Slice:
183 if isValueKind(ft.Elem().Kind()) {
184 addListOfParams(result, tag, omitempty, field)
185 }
186 case isStructKind(kind) && !(zeroValue(field) && omitempty):
187 if marshalValue, ok := customMarshalValue(field); ok {
188 addParam(result, tag, omitempty, marshalValue)
189 } else {
190 convertStruct(result, ft, field)
191 }
192 }
193 }
194 }
195
View as plain text