fielder.go 4.7 KB

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