github.com/opentelekomcloud/gophertelekomcloud@v0.9.3/internal/build/headers.go (about) 1 package build 2 3 import ( 4 "fmt" 5 "reflect" 6 "strconv" 7 8 "github.com/opentelekomcloud/gophertelekomcloud/internal/multierr" 9 ) 10 11 /* 12 Headers is an internal function to be used by request methods in 13 individual resource packages. 14 15 It accepts an arbitrary tagged structure and produces a string map that's 16 suitable for use as the HTTP headers of an outgoing request. Field names are 17 mapped to header names based in "h" tags. 18 19 type struct QueryStruct { 20 Bar string `h:"x_bar"` 21 Baz int `h:"lorem_ipsum"` 22 } 23 24 instance := QueryStruct{ 25 Bar: "AAA", 26 Baz: "BBB", 27 } 28 29 will be converted into: 30 31 map[string]string{ 32 "x_bar": "AAA", 33 "lorem_ipsum": "BBB", 34 } 35 36 Untagged fields and fields left at their zero values are skipped. Integers, 37 booleans and string values are supported. 38 */ 39 func Headers(opts interface{}) (map[string]string, error) { 40 if opts == nil { 41 return nil, fmt.Errorf("error building headers: %w", ErrNilOpts) 42 } 43 44 optsValue := reflect.ValueOf(opts) 45 if optsValue.Kind() == reflect.Ptr { 46 optsValue = optsValue.Elem() 47 } 48 49 optsType := reflect.TypeOf(opts) 50 if optsType.Kind() == reflect.Ptr { 51 optsType = optsType.Elem() 52 } 53 54 if optsValue.Kind() != reflect.Struct { 55 // Return an error if the underlying type of 'opts' isn't a struct. 56 return nil, fmt.Errorf("error building headers: options type is not a struct") 57 } 58 59 mErr := multierr.MultiError{} 60 result := make(map[string]string) 61 62 for i := 0; i < optsValue.NumField(); i++ { 63 value := optsValue.Field(i) 64 field := optsType.Field(i) 65 66 headerName := field.Tag.Get("h") 67 if headerName == "" { 68 continue 69 } 70 71 if value.IsZero() { 72 // We duplicate the check from ValidateTags to avoid double reflect package usage 73 // TODO: investigate performance difference when using ValidateTags 74 if structFieldRequired(field) { 75 mErr = append(mErr, fmt.Errorf("required header [%s] not set", field.Name)) 76 } 77 continue 78 } 79 80 if value.Kind() == reflect.Ptr { 81 value = value.Elem() 82 } 83 84 var headerValue string 85 86 // if the field is set, add it to the slice of query pieces 87 switch value.Kind() { 88 case reflect.String: 89 headerValue = value.String() 90 case reflect.Int, reflect.Int32, reflect.Int64: 91 headerValue = strconv.FormatInt(value.Int(), 10) 92 case reflect.Bool: 93 headerValue = strconv.FormatBool(value.Bool()) 94 default: 95 mErr = append(mErr, fmt.Errorf("value of unsupported type %s", value.Type())) 96 } 97 98 result[headerName] = headerValue 99 } 100 101 if err := mErr.ErrorOrNil(); err != nil { 102 return nil, fmt.Errorf("error building headers: %w", err) 103 } 104 105 return result, nil 106 }