decode_hooks.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. package mapstructure
  2. import (
  3. "errors"
  4. "reflect"
  5. "strconv"
  6. "strings"
  7. "time"
  8. )
  9. // typedDecodeHook takes a raw DecodeHookFunc (an interface{}) and turns
  10. // it into the proper DecodeHookFunc type, such as DecodeHookFuncType.
  11. func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc {
  12. // Create variables here so we can reference them with the reflect pkg
  13. var f1 DecodeHookFuncType
  14. var f2 DecodeHookFuncKind
  15. // Fill in the variables into this interface and the rest is done
  16. // automatically using the reflect package.
  17. potential := []interface{}{f1, f2}
  18. v := reflect.ValueOf(h)
  19. vt := v.Type()
  20. for _, raw := range potential {
  21. pt := reflect.ValueOf(raw).Type()
  22. if vt.ConvertibleTo(pt) {
  23. return v.Convert(pt).Interface()
  24. }
  25. }
  26. return nil
  27. }
  28. // DecodeHookExec executes the given decode hook. This should be used
  29. // since it'll naturally degrade to the older backwards compatible DecodeHookFunc
  30. // that took reflect.Kind instead of reflect.Type.
  31. func DecodeHookExec(
  32. raw DecodeHookFunc,
  33. from reflect.Type, to reflect.Type,
  34. data interface{}) (interface{}, error) {
  35. // Build our arguments that reflect expects
  36. argVals := make([]reflect.Value, 3)
  37. argVals[0] = reflect.ValueOf(from)
  38. argVals[1] = reflect.ValueOf(to)
  39. argVals[2] = reflect.ValueOf(data)
  40. switch f := typedDecodeHook(raw).(type) {
  41. case DecodeHookFuncType:
  42. return f(from, to, data)
  43. case DecodeHookFuncKind:
  44. return f(from.Kind(), to.Kind(), data)
  45. default:
  46. return nil, errors.New("invalid decode hook signature")
  47. }
  48. }
  49. // ComposeDecodeHookFunc creates a single DecodeHookFunc that
  50. // automatically composes multiple DecodeHookFuncs.
  51. //
  52. // The composed funcs are called in order, with the result of the
  53. // previous transformation.
  54. func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc {
  55. return func(
  56. f reflect.Type,
  57. t reflect.Type,
  58. data interface{}) (interface{}, error) {
  59. var err error
  60. for _, f1 := range fs {
  61. data, err = DecodeHookExec(f1, f, t, data)
  62. if err != nil {
  63. return nil, err
  64. }
  65. // Modify the from kind to be correct with the new data
  66. f = nil
  67. if val := reflect.ValueOf(data); val.IsValid() {
  68. f = val.Type()
  69. }
  70. }
  71. return data, nil
  72. }
  73. }
  74. // StringToSliceHookFunc returns a DecodeHookFunc that converts
  75. // string to []string by splitting on the given sep.
  76. func StringToSliceHookFunc(sep string) DecodeHookFunc {
  77. return func(
  78. f reflect.Kind,
  79. t reflect.Kind,
  80. data interface{}) (interface{}, error) {
  81. if f != reflect.String || t != reflect.Slice {
  82. return data, nil
  83. }
  84. raw := data.(string)
  85. if raw == "" {
  86. return []string{}, nil
  87. }
  88. return strings.Split(raw, sep), nil
  89. }
  90. }
  91. // StringToTimeDurationHookFunc returns a DecodeHookFunc that converts
  92. // strings to time.Duration.
  93. func StringToTimeDurationHookFunc() DecodeHookFunc {
  94. return func(
  95. f reflect.Type,
  96. t reflect.Type,
  97. data interface{}) (interface{}, error) {
  98. if f.Kind() != reflect.String {
  99. return data, nil
  100. }
  101. if t != reflect.TypeOf(time.Duration(5)) {
  102. return data, nil
  103. }
  104. // Convert it by parsing
  105. return time.ParseDuration(data.(string))
  106. }
  107. }
  108. func WeaklyTypedHook(
  109. f reflect.Kind,
  110. t reflect.Kind,
  111. data interface{}) (interface{}, error) {
  112. dataVal := reflect.ValueOf(data)
  113. switch t {
  114. case reflect.String:
  115. switch f {
  116. case reflect.Bool:
  117. if dataVal.Bool() {
  118. return "1", nil
  119. } else {
  120. return "0", nil
  121. }
  122. case reflect.Float32:
  123. return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil
  124. case reflect.Int:
  125. return strconv.FormatInt(dataVal.Int(), 10), nil
  126. case reflect.Slice:
  127. dataType := dataVal.Type()
  128. elemKind := dataType.Elem().Kind()
  129. if elemKind == reflect.Uint8 {
  130. return string(dataVal.Interface().([]uint8)), nil
  131. }
  132. case reflect.Uint:
  133. return strconv.FormatUint(dataVal.Uint(), 10), nil
  134. }
  135. }
  136. return data, nil
  137. }