encoder.go 4.8 KB

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