blob: da4ad94a32f8f3fc57ad64219f63666117497b5a [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
24var t time.Time
25
26func isZero(v reflect.Value) bool {
27 switch v.Kind() {
28 case reflect.Func, reflect.Map, reflect.Slice:
29 return v.IsNil()
30 case reflect.Array:
31 z := true
32 for i := 0; i < v.Len(); i++ {
33 z = z && isZero(v.Index(i))
34 }
35 return z
36 case reflect.Struct:
37 if v.Type() == reflect.TypeOf(t) {
38 if v.Interface().(time.Time).IsZero() {
39 return true
40 }
41 return false
42 }
43 z := true
44 for i := 0; i < v.NumField(); i++ {
45 z = z && isZero(v.Field(i))
46 }
47 return z
48 }
49 // Compare other types directly:
50 z := reflect.Zero(v.Type())
51 return v.Interface() == z.Interface()
52}
53
54func BuildQueryString(opts interface{}) (string, error) {
55 optsValue := reflect.ValueOf(opts)
56 if optsValue.Kind() == reflect.Ptr {
57 optsValue = optsValue.Elem()
58 }
59
60 optsType := reflect.TypeOf(opts)
61 if optsType.Kind() == reflect.Ptr {
62 optsType = optsType.Elem()
63 }
64
65 optsSlice := make([]string, 0)
66 if optsValue.Kind() == reflect.Struct {
67 for i := 0; i < optsValue.NumField(); i++ {
68 v := optsValue.Field(i)
69 f := optsType.Field(i)
70 qTag := f.Tag.Get("q")
71
72 // if the field has a 'q' tag, it goes in the query string
73 if qTag != "" {
74 tags := strings.Split(qTag, ",")
75
76 // if the field is set, add it to the slice of query pieces
77 if !isZero(v) {
78 switch v.Kind() {
79 case reflect.String:
80 optsSlice = append(optsSlice, tags[0]+"="+v.String())
81 case reflect.Int:
82 optsSlice = append(optsSlice, tags[0]+"="+strconv.FormatInt(v.Int(), 10))
83 case reflect.Bool:
84 optsSlice = append(optsSlice, tags[0]+"="+strconv.FormatBool(v.Bool()))
85 }
86 } else {
87 // Otherwise, the field is not set.
88 if len(tags) == 2 && tags[1] == "required" {
89 // And the field is required. Return an error.
90 return "", fmt.Errorf("Required query parameter not set.")
91 }
92 }
93 }
94
95 }
96 // URL encode the string for safety.
97 s := url.QueryEscape(strings.Join(optsSlice, "&"))
98 if s != "" {
99 s = "?" + s
100 }
101 return s, nil
102 }
103 // Return an error if the underlying type of 'opts' isn't a struct.
104 return "", fmt.Errorf("Options type is not a struct.")
105}
106
107func BuildRequestBody(opts interface{}) (map[string]interface{}, error) {
108 return nil, nil
109}
110
111func BuildHeaders(opts interface{}) (map[string]string, error) {
112 optsValue := reflect.ValueOf(opts)
113 if optsValue.Kind() == reflect.Ptr {
114 optsValue = optsValue.Elem()
115 }
116
117 optsType := reflect.TypeOf(opts)
118 if optsType.Kind() == reflect.Ptr {
119 optsType = optsType.Elem()
120 }
121
122 optsMap := make(map[string]string)
123 if optsValue.Kind() == reflect.Struct {
124 for i := 0; i < optsValue.NumField(); i++ {
125 v := optsValue.Field(i)
126 f := optsType.Field(i)
127 hTag := f.Tag.Get("h")
128
129 // if the field has a 'h' tag, it goes in the header
130 if hTag != "" {
131 tags := strings.Split(hTag, ",")
132
133 // if the field is set, add it to the slice of query pieces
134 if !isZero(v) {
135 switch v.Kind() {
136 case reflect.String:
137 optsMap[tags[0]] = v.String()
138 case reflect.Int:
139 optsMap[tags[0]] = strconv.FormatInt(v.Int(), 10)
140 case reflect.Bool:
141 optsMap[tags[0]] = strconv.FormatBool(v.Bool())
142 }
143 } else {
144 // Otherwise, the field is not set.
145 if len(tags) == 2 && tags[1] == "required" {
146 // And the field is required. Return an error.
147 return optsMap, fmt.Errorf("Required header not set.")
148 }
149 }
150 }
151
152 }
153 return optsMap, nil
154 }
155 // Return an error if the underlying type of 'opts' isn't a struct.
156 return optsMap, fmt.Errorf("Options type is not a struct.")
157}