fielder.go 5.4 KB

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