blob: 10aefea5447c03ce2c0229a695e0000dcff7bb98 [file] [log] [blame]
Jamie Hannaford6abf9282014-09-24 10:54:13 +02001package gophercloud
2
Jon Perrittf90a43c2014-09-28 20:09:46 -05003import (
4 "fmt"
5 "net/url"
6 "reflect"
7 "strconv"
8 "strings"
9 "time"
10)
11
Jamie Hannaford6abf9282014-09-24 10:54:13 +020012// MaybeString takes a string that might be a zero-value, and either returns a
13// pointer to its address or a nil value (i.e. empty pointer). This is useful
14// for converting zero values in options structs when the end-user hasn't
15// defined values. Those zero values need to be nil in order for the JSON
16// serialization to ignore them.
17func MaybeString(original string) *string {
18 if original != "" {
19 return &original
20 }
21 return nil
22}
Jon Perrittf90a43c2014-09-28 20:09:46 -050023
Jon Perritt8d262582014-10-03 11:11:46 -050024func MaybeInt(original int) *int {
25 if original != 0 {
26 return &original
27 }
28 return nil
29}
30
Jon Perrittf90a43c2014-09-28 20:09:46 -050031var t time.Time
32
33func isZero(v reflect.Value) bool {
34 switch v.Kind() {
35 case reflect.Func, reflect.Map, reflect.Slice:
36 return v.IsNil()
37 case reflect.Array:
38 z := true
39 for i := 0; i < v.Len(); i++ {
40 z = z && isZero(v.Index(i))
41 }
42 return z
43 case reflect.Struct:
44 if v.Type() == reflect.TypeOf(t) {
45 if v.Interface().(time.Time).IsZero() {
46 return true
47 }
48 return false
49 }
50 z := true
51 for i := 0; i < v.NumField(); i++ {
52 z = z && isZero(v.Field(i))
53 }
54 return z
55 }
56 // Compare other types directly:
57 z := reflect.Zero(v.Type())
58 return v.Interface() == z.Interface()
59}
60
Jon Perrittde47eac2014-09-30 15:34:17 -050061func BuildQueryString(opts interface{}) (*url.URL, error) {
Jon Perrittf90a43c2014-09-28 20:09:46 -050062 optsValue := reflect.ValueOf(opts)
63 if optsValue.Kind() == reflect.Ptr {
64 optsValue = optsValue.Elem()
65 }
66
67 optsType := reflect.TypeOf(opts)
68 if optsType.Kind() == reflect.Ptr {
69 optsType = optsType.Elem()
70 }
71
72 optsSlice := make([]string, 0)
73 if optsValue.Kind() == reflect.Struct {
74 for i := 0; i < optsValue.NumField(); i++ {
75 v := optsValue.Field(i)
76 f := optsType.Field(i)
77 qTag := f.Tag.Get("q")
78
79 // if the field has a 'q' tag, it goes in the query string
80 if qTag != "" {
81 tags := strings.Split(qTag, ",")
82
83 // if the field is set, add it to the slice of query pieces
84 if !isZero(v) {
85 switch v.Kind() {
86 case reflect.String:
87 optsSlice = append(optsSlice, tags[0]+"="+v.String())
88 case reflect.Int:
89 optsSlice = append(optsSlice, tags[0]+"="+strconv.FormatInt(v.Int(), 10))
90 case reflect.Bool:
91 optsSlice = append(optsSlice, tags[0]+"="+strconv.FormatBool(v.Bool()))
92 }
93 } else {
94 // Otherwise, the field is not set.
95 if len(tags) == 2 && tags[1] == "required" {
96 // And the field is required. Return an error.
Jon Perrittdb00ad12014-09-30 16:29:50 -050097 return nil, fmt.Errorf("Required query parameter [%s] not set.", f.Name)
Jon Perrittf90a43c2014-09-28 20:09:46 -050098 }
99 }
100 }
101
102 }
103 // URL encode the string for safety.
Jon Perritt255b6f82014-09-30 16:07:50 -0500104 s := strings.Join(optsSlice, "&")
Jon Perrittf90a43c2014-09-28 20:09:46 -0500105 if s != "" {
106 s = "?" + s
107 }
Jon Perrittde47eac2014-09-30 15:34:17 -0500108 u, err := url.Parse(s)
109 if err != nil {
110 return nil, err
111 }
112 return u, nil
Jon Perrittf90a43c2014-09-28 20:09:46 -0500113 }
114 // Return an error if the underlying type of 'opts' isn't a struct.
Jon Perrittde47eac2014-09-30 15:34:17 -0500115 return nil, fmt.Errorf("Options type is not a struct.")
Jon Perrittf90a43c2014-09-28 20:09:46 -0500116}
117
118func BuildRequestBody(opts interface{}) (map[string]interface{}, error) {
119 return nil, nil
120}
121
122func BuildHeaders(opts interface{}) (map[string]string, error) {
123 optsValue := reflect.ValueOf(opts)
124 if optsValue.Kind() == reflect.Ptr {
125 optsValue = optsValue.Elem()
126 }
127
128 optsType := reflect.TypeOf(opts)
129 if optsType.Kind() == reflect.Ptr {
130 optsType = optsType.Elem()
131 }
132
133 optsMap := make(map[string]string)
134 if optsValue.Kind() == reflect.Struct {
135 for i := 0; i < optsValue.NumField(); i++ {
136 v := optsValue.Field(i)
137 f := optsType.Field(i)
138 hTag := f.Tag.Get("h")
139
140 // if the field has a 'h' tag, it goes in the header
141 if hTag != "" {
142 tags := strings.Split(hTag, ",")
143
144 // if the field is set, add it to the slice of query pieces
145 if !isZero(v) {
146 switch v.Kind() {
147 case reflect.String:
148 optsMap[tags[0]] = v.String()
149 case reflect.Int:
150 optsMap[tags[0]] = strconv.FormatInt(v.Int(), 10)
151 case reflect.Bool:
152 optsMap[tags[0]] = strconv.FormatBool(v.Bool())
153 }
154 } else {
155 // Otherwise, the field is not set.
156 if len(tags) == 2 && tags[1] == "required" {
157 // And the field is required. Return an error.
158 return optsMap, fmt.Errorf("Required header not set.")
159 }
160 }
161 }
162
163 }
164 return optsMap, nil
165 }
166 // Return an error if the underlying type of 'opts' isn't a struct.
167 return optsMap, fmt.Errorf("Options type is not a struct.")
168}