fielder.go 4.7 KB

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