本站源代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

218 lines
5.1KB

  1. package mapstructure
  2. import (
  3. "errors"
  4. "fmt"
  5. "net"
  6. "reflect"
  7. "strconv"
  8. "strings"
  9. "time"
  10. )
  11. // typedDecodeHook takes a raw DecodeHookFunc (an interface{}) and turns
  12. // it into the proper DecodeHookFunc type, such as DecodeHookFuncType.
  13. func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc {
  14. // Create variables here so we can reference them with the reflect pkg
  15. var f1 DecodeHookFuncType
  16. var f2 DecodeHookFuncKind
  17. // Fill in the variables into this interface and the rest is done
  18. // automatically using the reflect package.
  19. potential := []interface{}{f1, f2}
  20. v := reflect.ValueOf(h)
  21. vt := v.Type()
  22. for _, raw := range potential {
  23. pt := reflect.ValueOf(raw).Type()
  24. if vt.ConvertibleTo(pt) {
  25. return v.Convert(pt).Interface()
  26. }
  27. }
  28. return nil
  29. }
  30. // DecodeHookExec executes the given decode hook. This should be used
  31. // since it'll naturally degrade to the older backwards compatible DecodeHookFunc
  32. // that took reflect.Kind instead of reflect.Type.
  33. func DecodeHookExec(
  34. raw DecodeHookFunc,
  35. from reflect.Type, to reflect.Type,
  36. data interface{}) (interface{}, error) {
  37. switch f := typedDecodeHook(raw).(type) {
  38. case DecodeHookFuncType:
  39. return f(from, to, data)
  40. case DecodeHookFuncKind:
  41. return f(from.Kind(), to.Kind(), data)
  42. default:
  43. return nil, errors.New("invalid decode hook signature")
  44. }
  45. }
  46. // ComposeDecodeHookFunc creates a single DecodeHookFunc that
  47. // automatically composes multiple DecodeHookFuncs.
  48. //
  49. // The composed funcs are called in order, with the result of the
  50. // previous transformation.
  51. func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc {
  52. return func(
  53. f reflect.Type,
  54. t reflect.Type,
  55. data interface{}) (interface{}, error) {
  56. var err error
  57. for _, f1 := range fs {
  58. data, err = DecodeHookExec(f1, f, t, data)
  59. if err != nil {
  60. return nil, err
  61. }
  62. // Modify the from kind to be correct with the new data
  63. f = nil
  64. if val := reflect.ValueOf(data); val.IsValid() {
  65. f = val.Type()
  66. }
  67. }
  68. return data, nil
  69. }
  70. }
  71. // StringToSliceHookFunc returns a DecodeHookFunc that converts
  72. // string to []string by splitting on the given sep.
  73. func StringToSliceHookFunc(sep string) DecodeHookFunc {
  74. return func(
  75. f reflect.Kind,
  76. t reflect.Kind,
  77. data interface{}) (interface{}, error) {
  78. if f != reflect.String || t != reflect.Slice {
  79. return data, nil
  80. }
  81. raw := data.(string)
  82. if raw == "" {
  83. return []string{}, nil
  84. }
  85. return strings.Split(raw, sep), nil
  86. }
  87. }
  88. // StringToTimeDurationHookFunc returns a DecodeHookFunc that converts
  89. // strings to time.Duration.
  90. func StringToTimeDurationHookFunc() DecodeHookFunc {
  91. return func(
  92. f reflect.Type,
  93. t reflect.Type,
  94. data interface{}) (interface{}, error) {
  95. if f.Kind() != reflect.String {
  96. return data, nil
  97. }
  98. if t != reflect.TypeOf(time.Duration(5)) {
  99. return data, nil
  100. }
  101. // Convert it by parsing
  102. return time.ParseDuration(data.(string))
  103. }
  104. }
  105. // StringToIPHookFunc returns a DecodeHookFunc that converts
  106. // strings to net.IP
  107. func StringToIPHookFunc() DecodeHookFunc {
  108. return func(
  109. f reflect.Type,
  110. t reflect.Type,
  111. data interface{}) (interface{}, error) {
  112. if f.Kind() != reflect.String {
  113. return data, nil
  114. }
  115. if t != reflect.TypeOf(net.IP{}) {
  116. return data, nil
  117. }
  118. // Convert it by parsing
  119. ip := net.ParseIP(data.(string))
  120. if ip == nil {
  121. return net.IP{}, fmt.Errorf("failed parsing ip %v", data)
  122. }
  123. return ip, nil
  124. }
  125. }
  126. // StringToIPNetHookFunc returns a DecodeHookFunc that converts
  127. // strings to net.IPNet
  128. func StringToIPNetHookFunc() DecodeHookFunc {
  129. return func(
  130. f reflect.Type,
  131. t reflect.Type,
  132. data interface{}) (interface{}, error) {
  133. if f.Kind() != reflect.String {
  134. return data, nil
  135. }
  136. if t != reflect.TypeOf(net.IPNet{}) {
  137. return data, nil
  138. }
  139. // Convert it by parsing
  140. _, net, err := net.ParseCIDR(data.(string))
  141. return net, err
  142. }
  143. }
  144. // StringToTimeHookFunc returns a DecodeHookFunc that converts
  145. // strings to time.Time.
  146. func StringToTimeHookFunc(layout string) DecodeHookFunc {
  147. return func(
  148. f reflect.Type,
  149. t reflect.Type,
  150. data interface{}) (interface{}, error) {
  151. if f.Kind() != reflect.String {
  152. return data, nil
  153. }
  154. if t != reflect.TypeOf(time.Time{}) {
  155. return data, nil
  156. }
  157. // Convert it by parsing
  158. return time.Parse(layout, data.(string))
  159. }
  160. }
  161. // WeaklyTypedHook is a DecodeHookFunc which adds support for weak typing to
  162. // the decoder.
  163. //
  164. // Note that this is significantly different from the WeaklyTypedInput option
  165. // of the DecoderConfig.
  166. func WeaklyTypedHook(
  167. f reflect.Kind,
  168. t reflect.Kind,
  169. data interface{}) (interface{}, error) {
  170. dataVal := reflect.ValueOf(data)
  171. switch t {
  172. case reflect.String:
  173. switch f {
  174. case reflect.Bool:
  175. if dataVal.Bool() {
  176. return "1", nil
  177. }
  178. return "0", nil
  179. case reflect.Float32:
  180. return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil
  181. case reflect.Int:
  182. return strconv.FormatInt(dataVal.Int(), 10), nil
  183. case reflect.Slice:
  184. dataType := dataVal.Type()
  185. elemKind := dataType.Elem().Kind()
  186. if elemKind == reflect.Uint8 {
  187. return string(dataVal.Interface().([]uint8)), nil
  188. }
  189. case reflect.Uint:
  190. return strconv.FormatUint(dataVal.Uint(), 10), nil
  191. }
  192. }
  193. return data, nil
  194. }
上海开阖软件有限公司 沪ICP备12045867号-1