fielder.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. package rest
  2. import (
  3. "fmt"
  4. "io"
  5. "reflect"
  6. "strings"
  7. "git.ali33.ru/fcg-xvii/go-tools/json"
  8. )
  9. // Field реализует ...
  10. type Field struct {
  11. Name string
  12. Names []any
  13. }
  14. type FieldList []*Field
  15. func (s FieldList) Field(name string) (*Field, bool) {
  16. for _, f := range s {
  17. if f.Name == name {
  18. return f, true
  19. }
  20. }
  21. return nil, false
  22. }
  23. // IFielder реадизует интерфейс завершения формирования полей объекта в "ручном" режиме.
  24. // RestFields будет вызван после завершения автматического формирования полей объекта
  25. // result - массив с полями, сформированными автоматически, в него можно вносить правки
  26. // files - глобальный массив файловых дескрипторов, который будет передан в ответе клиенту
  27. type IFielder interface {
  28. RestFields(result json.Map, files map[string]io.ReadCloser, names FieldList)
  29. }
  30. func fieldsDefault(t reflect.Type) (res []any) {
  31. for i := 0; i < t.NumField(); i++ {
  32. fType := t.Field(i)
  33. // проверяем тег, если он есть
  34. tag := fType.Tag.Get("rest")
  35. if len(tag) > 0 && strings.Contains(tag, "default") {
  36. res = append(res, camelToSnake(fType.Name))
  37. }
  38. }
  39. return
  40. }
  41. func parseName(val reflect.Value) (res *Field, err error) {
  42. switch val.Kind() {
  43. case reflect.String:
  44. return &Field{
  45. Name: camelToSnake(val.String()),
  46. }, nil
  47. case reflect.Map:
  48. jm := make(json.Map)
  49. jmVal := reflect.ValueOf(jm)
  50. if !val.CanConvert(jmVal.Type()) {
  51. err = fmt.Errorf("expected map[string]any")
  52. }
  53. jm = val.Convert(jmVal.Type()).Interface().(json.Map)
  54. name := jm.String("name", "")
  55. if len(name) == 0 {
  56. err = fmt.Errorf("name is empty")
  57. return
  58. }
  59. name = camelToSnake(name)
  60. fields := jm.Slice("fields", nil)
  61. res = &Field{
  62. Name: name,
  63. Names: fields,
  64. }
  65. return
  66. default:
  67. err = fmt.Errorf("invalid request %s", val.Kind())
  68. }
  69. return
  70. }
  71. func fieldVal(val reflect.Value, fieldName string, files map[string]io.ReadCloser, names ...any) (res reflect.Value, err IErrorArgs) {
  72. switch val.Kind() {
  73. case reflect.Ptr, reflect.Interface:
  74. return fieldVal(val.Elem(), fieldName, files, names...)
  75. case reflect.Struct:
  76. if len(names) == 0 {
  77. names = fieldsDefault(val.Type())
  78. }
  79. jm := json.Map{}
  80. var fields FieldList
  81. loop:
  82. for _, name := range names {
  83. rField, rErr := parseName(reflect.ValueOf(name))
  84. if rErr != nil {
  85. message := fmt.Sprintf("%v: %v", name, err)
  86. err = ErrorFiled(fieldName, message)
  87. return
  88. }
  89. fields = append(fields, rField)
  90. var field reflect.Value
  91. for i := 0; i < val.NumField(); i++ {
  92. f := val.Type().Field(i)
  93. if camelToSnake(f.Name) == rField.Name {
  94. tag := f.Tag.Get("rest")
  95. if len(tag) > 0 && strings.Contains(tag, "ignore") {
  96. continue loop
  97. }
  98. field = val.Field(i)
  99. break
  100. }
  101. }
  102. if !field.IsValid() {
  103. err = ErrorFiled(rField.Name, "field is not found")
  104. return
  105. }
  106. var fVal reflect.Value
  107. if fVal, err = fieldVal(
  108. field,
  109. fmt.Sprintf("%v.%v", fieldName, rField.Name),
  110. files,
  111. rField.Names...,
  112. ); err != nil {
  113. return
  114. }
  115. jm[rField.Name] = fVal.Interface()
  116. }
  117. if fielder, check := val.Interface().(IFielder); check {
  118. fielder.RestFields(jm, files, fields)
  119. } else if val.CanAddr() {
  120. if fielder, check := val.Addr().Interface().(IFielder); check {
  121. fielder.RestFields(jm, files, fields)
  122. }
  123. }
  124. res = reflect.ValueOf(jm)
  125. return
  126. case reflect.Slice:
  127. sl := make([]any, val.Len())
  128. for i := 0; i < val.Len(); i++ {
  129. fName := fmt.Sprintf("%v[%v]", fieldName, i)
  130. var rVal reflect.Value
  131. rVal, err = fieldVal(
  132. val.Index(i),
  133. fName,
  134. files,
  135. names...,
  136. )
  137. if err != nil {
  138. return
  139. }
  140. sl[i] = rVal.Interface()
  141. }
  142. res = reflect.ValueOf(sl)
  143. return
  144. case reflect.Map:
  145. res = reflect.MakeMap(val.Type())
  146. for _, key := range val.MapKeys() {
  147. fName := fmt.Sprintf("%v[%v]", fieldName, key.String())
  148. var rVal reflect.Value
  149. iVal := val.MapIndex(key)
  150. rVal, err = fieldVal(iVal, fName, files)
  151. if err != nil {
  152. return
  153. }
  154. res.SetMapIndex(key, rVal)
  155. }
  156. return
  157. default:
  158. res = val
  159. return
  160. }
  161. }
  162. // Fields позволяет получить значения объекта в json
  163. func Fields(obj any, files map[string]io.ReadCloser, names ...any) (json.Map, IErrorArgs) {
  164. sVal := reflect.ValueOf(obj)
  165. rVal, err := fieldVal(sVal.Elem(), "", files, names...)
  166. if err != nil {
  167. return nil, err
  168. }
  169. res, check := rVal.Interface().(json.Map)
  170. if !check {
  171. return nil, ErrorMessage("ErrFields", "Empty object")
  172. }
  173. return res, nil
  174. }
  175. /////////////////////////////////////////////