encoder.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. package schema
  2. import (
  3. "errors"
  4. "fmt"
  5. "reflect"
  6. "strconv"
  7. utils "github.com/gofiber/utils/v2"
  8. )
  9. type encoderFunc func(reflect.Value) string
  10. // Encoder encodes values from a struct into url.Values.
  11. type Encoder struct {
  12. cache *cache
  13. regenc map[reflect.Type]encoderFunc
  14. }
  15. // NewEncoder returns a new Encoder with defaults.
  16. func NewEncoder() *Encoder {
  17. return &Encoder{cache: newCache(), regenc: make(map[reflect.Type]encoderFunc)}
  18. }
  19. // Encode encodes a struct into map[string][]string.
  20. //
  21. // Intended for use with url.Values.
  22. func (e *Encoder) Encode(src interface{}, dst map[string][]string) error {
  23. v := reflect.ValueOf(src)
  24. return e.encode(v, dst)
  25. }
  26. // RegisterEncoder registers a converter for encoding a custom type.
  27. func (e *Encoder) RegisterEncoder(value interface{}, encoder func(reflect.Value) string) {
  28. e.regenc[reflect.TypeOf(value)] = encoder
  29. }
  30. // SetAliasTag changes the tag used to locate custom field aliases.
  31. // The default tag is "schema".
  32. func (e *Encoder) SetAliasTag(tag string) {
  33. e.cache.tag = tag
  34. }
  35. // isValidStructPointer test if input value is a valid struct pointer.
  36. func isValidStructPointer(v reflect.Value) bool {
  37. return v.Type().Kind() == reflect.Ptr && v.Elem().IsValid() && v.Elem().Type().Kind() == reflect.Struct
  38. }
  39. func isZero(v reflect.Value) bool {
  40. switch v.Kind() {
  41. case reflect.Func:
  42. case reflect.Map, reflect.Slice:
  43. return v.IsNil() || v.Len() == 0
  44. case reflect.Array:
  45. z := true
  46. for i := 0; i < v.Len(); i++ {
  47. z = z && isZero(v.Index(i))
  48. }
  49. return z
  50. case reflect.Struct:
  51. type zero interface {
  52. IsZero() bool
  53. }
  54. if v.Type().Implements(reflect.TypeOf((*zero)(nil)).Elem()) {
  55. iz, _ := reflect.TypeAssert[bool](v.MethodByName("IsZero").Call([]reflect.Value{})[0])
  56. return iz
  57. }
  58. z := true
  59. for i := 0; i < v.NumField(); i++ {
  60. z = z && isZero(v.Field(i))
  61. }
  62. return z
  63. }
  64. // Compare other types directly:
  65. z := reflect.Zero(v.Type())
  66. return v.Interface() == z.Interface()
  67. }
  68. func (e *Encoder) encode(v reflect.Value, dst map[string][]string) error {
  69. if v.Kind() == reflect.Ptr {
  70. v = v.Elem()
  71. }
  72. if v.Kind() != reflect.Struct {
  73. return errors.New("schema: interface must be a struct")
  74. }
  75. t := v.Type()
  76. errors := MultiError{}
  77. for i := 0; i < v.NumField(); i++ {
  78. fieldValue := v.Field(i)
  79. fieldType := fieldValue.Type()
  80. name, opts := fieldAlias(t.Field(i), e.cache.tag)
  81. if name == "-" {
  82. continue
  83. }
  84. // Encode struct pointer types if the field is a valid pointer and a struct.
  85. if isValidStructPointer(fieldValue) && !e.hasCustomEncoder(fieldType) {
  86. err := e.encode(fieldValue.Elem(), dst)
  87. if err != nil {
  88. errors[fieldValue.Elem().Type().String()] = err
  89. }
  90. continue
  91. }
  92. encFunc := typeEncoder(fieldType, e.regenc)
  93. // Encode non-slice types and custom implementations immediately.
  94. if encFunc != nil {
  95. value := encFunc(fieldValue)
  96. if opts.Contains("omitempty") && isZero(fieldValue) {
  97. continue
  98. }
  99. dst[name] = append(dst[name], value)
  100. continue
  101. }
  102. if fieldType.Kind() == reflect.Struct {
  103. err := e.encode(fieldValue, dst)
  104. if err != nil {
  105. errors[fieldType.String()] = err
  106. }
  107. continue
  108. }
  109. if fieldType.Kind() == reflect.Slice {
  110. encFunc = typeEncoder(fieldType.Elem(), e.regenc)
  111. }
  112. if encFunc == nil {
  113. errors[fieldType.String()] = fmt.Errorf("schema: encoder not found for %v", fieldValue)
  114. continue
  115. }
  116. // Encode a slice.
  117. if fieldValue.Len() == 0 && opts.Contains("omitempty") {
  118. continue
  119. }
  120. dst[name] = []string{}
  121. for j := 0; j < fieldValue.Len(); j++ {
  122. dst[name] = append(dst[name], encFunc(fieldValue.Index(j)))
  123. }
  124. }
  125. if len(errors) > 0 {
  126. return errors
  127. }
  128. return nil
  129. }
  130. func (e *Encoder) hasCustomEncoder(t reflect.Type) bool {
  131. _, exists := e.regenc[t]
  132. return exists
  133. }
  134. func typeEncoder(t reflect.Type, reg map[reflect.Type]encoderFunc) encoderFunc {
  135. if f, ok := reg[t]; ok {
  136. return f
  137. }
  138. switch t.Kind() {
  139. case reflect.Bool:
  140. return encodeBool
  141. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  142. return encodeInt
  143. case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
  144. return encodeUint
  145. case reflect.Float32:
  146. return encodeFloat32
  147. case reflect.Float64:
  148. return encodeFloat64
  149. case reflect.Ptr:
  150. f := typeEncoder(t.Elem(), reg)
  151. return func(v reflect.Value) string {
  152. if v.IsNil() {
  153. return "null"
  154. }
  155. return f(v.Elem())
  156. }
  157. case reflect.String:
  158. return encodeString
  159. default:
  160. return nil
  161. }
  162. }
  163. func encodeBool(v reflect.Value) string {
  164. return strconv.FormatBool(v.Bool())
  165. }
  166. func encodeInt(v reflect.Value) string {
  167. return utils.FormatInt(v.Int())
  168. }
  169. func encodeUint(v reflect.Value) string {
  170. return utils.FormatUint(v.Uint())
  171. }
  172. func encodeFloat(v reflect.Value, bits int) string {
  173. return strconv.FormatFloat(v.Float(), 'f', 6, bits)
  174. }
  175. func encodeFloat32(v reflect.Value) string {
  176. return encodeFloat(v, 32)
  177. }
  178. func encodeFloat64(v reflect.Value) string {
  179. return encodeFloat(v, 64)
  180. }
  181. func encodeString(v reflect.Value) string {
  182. return v.String()
  183. }