|  | package gophercloud | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "net/url" | 
|  | "reflect" | 
|  | "strconv" | 
|  | "strings" | 
|  | "time" | 
|  | ) | 
|  |  | 
|  | // MaybeString takes a string that might be a zero-value, and either returns a | 
|  | // pointer to its address or a nil value (i.e. empty pointer). This is useful | 
|  | // for converting zero values in options structs when the end-user hasn't | 
|  | // defined values. Those zero values need to be nil in order for the JSON | 
|  | // serialization to ignore them. | 
|  | func MaybeString(original string) *string { | 
|  | if original != "" { | 
|  | return &original | 
|  | } | 
|  | return nil | 
|  | } | 
|  |  | 
|  | // MaybeInt takes an int that might be a zero-value, and either returns a | 
|  | // pointer to its address or a nil value (i.e. empty pointer). | 
|  | func MaybeInt(original int) *int { | 
|  | if original != 0 { | 
|  | return &original | 
|  | } | 
|  | return nil | 
|  | } | 
|  |  | 
|  | var t time.Time | 
|  |  | 
|  | func isZero(v reflect.Value) bool { | 
|  | switch v.Kind() { | 
|  | case reflect.Func, reflect.Map, reflect.Slice: | 
|  | return v.IsNil() | 
|  | case reflect.Array: | 
|  | z := true | 
|  | for i := 0; i < v.Len(); i++ { | 
|  | z = z && isZero(v.Index(i)) | 
|  | } | 
|  | return z | 
|  | case reflect.Struct: | 
|  | if v.Type() == reflect.TypeOf(t) { | 
|  | if v.Interface().(time.Time).IsZero() { | 
|  | return true | 
|  | } | 
|  | return false | 
|  | } | 
|  | z := true | 
|  | for i := 0; i < v.NumField(); i++ { | 
|  | z = z && isZero(v.Field(i)) | 
|  | } | 
|  | return z | 
|  | } | 
|  | // Compare other types directly: | 
|  | z := reflect.Zero(v.Type()) | 
|  | return v.Interface() == z.Interface() | 
|  | } | 
|  |  | 
|  | // BuildQueryString accepts a generic structure and parses it URL struct. It | 
|  | // converts field names into query names based on tags. So for example, this | 
|  | // type: | 
|  | // | 
|  | // struct { | 
|  | //    Bar string `q:"x_bar"` | 
|  | //    Baz int    `q:"lorem_ipsum"` | 
|  | // }{ | 
|  | //    Bar: "XXX", | 
|  | //    Baz: "YYY", | 
|  | // } | 
|  | // | 
|  | // will be converted into ?x_bar=XXX&lorem_ipsum=YYYY | 
|  | func BuildQueryString(opts interface{}) (*url.URL, error) { | 
|  | optsValue := reflect.ValueOf(opts) | 
|  | if optsValue.Kind() == reflect.Ptr { | 
|  | optsValue = optsValue.Elem() | 
|  | } | 
|  |  | 
|  | optsType := reflect.TypeOf(opts) | 
|  | if optsType.Kind() == reflect.Ptr { | 
|  | optsType = optsType.Elem() | 
|  | } | 
|  |  | 
|  | var optsSlice []string | 
|  | if optsValue.Kind() == reflect.Struct { | 
|  | for i := 0; i < optsValue.NumField(); i++ { | 
|  | v := optsValue.Field(i) | 
|  | f := optsType.Field(i) | 
|  | qTag := f.Tag.Get("q") | 
|  |  | 
|  | // if the field has a 'q' tag, it goes in the query string | 
|  | if qTag != "" { | 
|  | tags := strings.Split(qTag, ",") | 
|  |  | 
|  | // if the field is set, add it to the slice of query pieces | 
|  | if !isZero(v) { | 
|  | switch v.Kind() { | 
|  | case reflect.String: | 
|  | optsSlice = append(optsSlice, tags[0]+"="+v.String()) | 
|  | case reflect.Int: | 
|  | optsSlice = append(optsSlice, tags[0]+"="+strconv.FormatInt(v.Int(), 10)) | 
|  | case reflect.Bool: | 
|  | optsSlice = append(optsSlice, tags[0]+"="+strconv.FormatBool(v.Bool())) | 
|  | } | 
|  | } else { | 
|  | // Otherwise, the field is not set. | 
|  | if len(tags) == 2 && tags[1] == "required" { | 
|  | // And the field is required. Return an error. | 
|  | return nil, fmt.Errorf("Required query parameter [%s] not set.", f.Name) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | } | 
|  | // URL encode the string for safety. | 
|  | s := strings.Join(optsSlice, "&") | 
|  | if s != "" { | 
|  | s = "?" + s | 
|  | } | 
|  | u, err := url.Parse(s) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | return u, nil | 
|  | } | 
|  | // Return an error if the underlying type of 'opts' isn't a struct. | 
|  | return nil, fmt.Errorf("Options type is not a struct.") | 
|  | } | 
|  |  | 
|  | // BuildHeaders accepts a generic structure and parses it string map. It | 
|  | // converts field names into header names based on "h" tags, and field values | 
|  | // into header values by a simple one-to-one mapping. | 
|  | func BuildHeaders(opts interface{}) (map[string]string, error) { | 
|  | optsValue := reflect.ValueOf(opts) | 
|  | if optsValue.Kind() == reflect.Ptr { | 
|  | optsValue = optsValue.Elem() | 
|  | } | 
|  |  | 
|  | optsType := reflect.TypeOf(opts) | 
|  | if optsType.Kind() == reflect.Ptr { | 
|  | optsType = optsType.Elem() | 
|  | } | 
|  |  | 
|  | optsMap := make(map[string]string) | 
|  | if optsValue.Kind() == reflect.Struct { | 
|  | for i := 0; i < optsValue.NumField(); i++ { | 
|  | v := optsValue.Field(i) | 
|  | f := optsType.Field(i) | 
|  | hTag := f.Tag.Get("h") | 
|  |  | 
|  | // if the field has a 'h' tag, it goes in the header | 
|  | if hTag != "" { | 
|  | tags := strings.Split(hTag, ",") | 
|  |  | 
|  | // if the field is set, add it to the slice of query pieces | 
|  | if !isZero(v) { | 
|  | switch v.Kind() { | 
|  | case reflect.String: | 
|  | optsMap[tags[0]] = v.String() | 
|  | case reflect.Int: | 
|  | optsMap[tags[0]] = strconv.FormatInt(v.Int(), 10) | 
|  | case reflect.Bool: | 
|  | optsMap[tags[0]] = strconv.FormatBool(v.Bool()) | 
|  | } | 
|  | } else { | 
|  | // Otherwise, the field is not set. | 
|  | if len(tags) == 2 && tags[1] == "required" { | 
|  | // And the field is required. Return an error. | 
|  | return optsMap, fmt.Errorf("Required header not set.") | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | } | 
|  | return optsMap, nil | 
|  | } | 
|  | // Return an error if the underlying type of 'opts' isn't a struct. | 
|  | return optsMap, fmt.Errorf("Options type is not a struct.") | 
|  | } |