|
@@ -1,84 +1,142 @@
|
|
package rest
|
|
package rest
|
|
|
|
|
|
import (
|
|
import (
|
|
- "io"
|
|
|
|
- "log"
|
|
|
|
|
|
+ "fmt"
|
|
"reflect"
|
|
"reflect"
|
|
"strings"
|
|
"strings"
|
|
-
|
|
|
|
- "git.ali33.ru/fcg-xvii/go-tools/json"
|
|
|
|
)
|
|
)
|
|
|
|
|
|
-func findFileKey(m map[string]io.ReadCloser, key string) (io.Reader, bool) {
|
|
|
|
- key = camelToSnake(key)
|
|
|
|
- for fKey, val := range m {
|
|
|
|
- fKey = strings.Replace(fKey, ".", "_", -1)
|
|
|
|
- if key == fKey {
|
|
|
|
- return val, true
|
|
|
|
- }
|
|
|
|
|
|
+func parseVal(from, to reflect.Value, fieldName string) IErrorArgs {
|
|
|
|
+ if from.Kind() == reflect.Interface {
|
|
|
|
+ from = from.Elem()
|
|
|
|
+ }
|
|
|
|
+ if !from.CanConvert(to.Type()) {
|
|
|
|
+ return NewErrFieldType(fieldName, from.Type().String(), to.Type().String(), "unexpected type", -1)
|
|
|
|
+ }
|
|
|
|
+ to.Set(from.Convert(to.Type()))
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func parseType(from, to reflect.Value, fieldName string) IErrorArgs {
|
|
|
|
+ from = realValue(from)
|
|
|
|
+ to = realValue(to)
|
|
|
|
+ switch to.Kind() {
|
|
|
|
+ case reflect.Interface:
|
|
|
|
+ to.Set(from)
|
|
|
|
+ return nil
|
|
|
|
+ case reflect.Struct:
|
|
|
|
+ return parseStruct(from, to, fieldName)
|
|
|
|
+ case reflect.Slice:
|
|
|
|
+ return parseSlice(from, to, fieldName)
|
|
|
|
+ case reflect.Map:
|
|
|
|
+ return parseMap(from, to, fieldName)
|
|
|
|
+ default:
|
|
|
|
+ return parseVal(from, to, fieldName)
|
|
}
|
|
}
|
|
- return nil, false
|
|
|
|
}
|
|
}
|
|
|
|
|
|
-func findKey(m map[string]any, key string) (val any, check bool) {
|
|
|
|
- if val, check = m[key]; !check {
|
|
|
|
- if !check {
|
|
|
|
- key = camelToSnake(key)
|
|
|
|
- val, check = m[key]
|
|
|
|
|
|
+func realValue(val reflect.Value) reflect.Value {
|
|
|
|
+ //log.Println(val.IsNil())
|
|
|
|
+ if val.Kind() == reflect.Ptr {
|
|
|
|
+ if val.IsNil() {
|
|
|
|
+ val.Set(reflect.New(val.Type().Elem()))
|
|
|
|
+ }
|
|
|
|
+ val = val.Elem()
|
|
|
|
+ if val.Kind() == reflect.Ptr {
|
|
|
|
+ return realValue(val)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- return val, check
|
|
|
|
|
|
+ return val
|
|
}
|
|
}
|
|
|
|
|
|
-func serializeRequest(obj any, data json.Map, files map[string]io.ReadCloser, parent string) IErrorArgs {
|
|
|
|
- data.LogPretty()
|
|
|
|
- val := reflect.ValueOf(obj)
|
|
|
|
- if val.Kind() != reflect.Ptr {
|
|
|
|
- return NewErrPrtexpected(parent)
|
|
|
|
|
|
+func parseSlice(from, to reflect.Value, fieldName string) IErrorArgs {
|
|
|
|
+ if to.IsNil() {
|
|
|
|
+ to.Set(reflect.MakeSlice(to.Type(), 0, from.Cap()))
|
|
|
|
+ }
|
|
|
|
+ eType := to.Type().Elem()
|
|
|
|
+ // Перебираем элементы среза, откуда копируем.
|
|
|
|
+ for i := 0; i < from.Len(); i++ {
|
|
|
|
+ iFieldName := fmt.Sprintf("%v[%v]", fieldName, i)
|
|
|
|
+ fromVal := from.Index(i)
|
|
|
|
+ toVal := reflect.New(eType).Elem()
|
|
|
|
+ if err := parseType(fromVal, toVal.Addr(), iFieldName); err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ to.Set(reflect.Append(to, toVal))
|
|
}
|
|
}
|
|
- el := val.Elem()
|
|
|
|
- for i := 0; i < el.NumField(); i++ {
|
|
|
|
- required, isFile, arrived := false, false, false
|
|
|
|
- field := el.Type().Field(i)
|
|
|
|
- log.Println("------>", field)
|
|
|
|
- xField := el.Field(i)
|
|
|
|
- tag := field.Tag.Get("rest")
|
|
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func parseStruct(from, to reflect.Value, fieldName string) IErrorArgs {
|
|
|
|
+ for i := 0; i < to.NumField(); i++ {
|
|
|
|
+ required := false
|
|
|
|
+ // устанавливаем тип и указатель поля объекта, куда копируем
|
|
|
|
+ fType := to.Type().Field(i)
|
|
|
|
+ fVal := to.Field(i)
|
|
|
|
+ // проверяем тег, если он есть
|
|
|
|
+ tag := fType.Tag.Get("rest")
|
|
if len(tag) > 0 {
|
|
if len(tag) > 0 {
|
|
required = strings.Contains(tag, "required")
|
|
required = strings.Contains(tag, "required")
|
|
- isFile = strings.Contains(tag, "file")
|
|
|
|
}
|
|
}
|
|
- if isFile {
|
|
|
|
- if f, check := findFileKey(files, field.Name); check {
|
|
|
|
- xField.Set(reflect.ValueOf(f))
|
|
|
|
- arrived = true
|
|
|
|
|
|
+ var rVal reflect.Value
|
|
|
|
+ switch from.Kind() {
|
|
|
|
+ case reflect.Struct:
|
|
|
|
+ if rVal = from.FieldByName(fType.Name); !rVal.IsValid() {
|
|
|
|
+ rVal = from.FieldByName(camelToSnake(fType.Name))
|
|
}
|
|
}
|
|
- } else {
|
|
|
|
- if cval, check := findKey(data, field.Name); check {
|
|
|
|
- rVal := reflect.ValueOf(cval)
|
|
|
|
- switch {
|
|
|
|
- case xField.Kind() == reflect.Ptr:
|
|
|
|
- if rVal.Kind() != reflect.Map {
|
|
|
|
- return NewErrFieldType(field.Name, xField.Type().String(), rVal.String(), "expected json object")
|
|
|
|
- }
|
|
|
|
- rv := reflect.New(xField.Type().Elem())
|
|
|
|
- m := rVal.Convert(reflect.TypeOf(json.Map{})).Interface()
|
|
|
|
- if err := serializeRequest(rv.Interface(), m.(json.Map), files, field.Name); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- xField.Set(rv)
|
|
|
|
- default:
|
|
|
|
- if rVal.CanConvert(xField.Type()) {
|
|
|
|
- xField.Set(rVal.Convert(xField.Type()))
|
|
|
|
- } else {
|
|
|
|
- return NewErrFieldType(field.Name, xField.Type().String(), rVal.Kind().String(), "convert error")
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- arrived = true
|
|
|
|
|
|
+ case reflect.Map:
|
|
|
|
+ if rVal = from.MapIndex(reflect.ValueOf(fType.Name)); !rVal.IsValid() {
|
|
|
|
+ rVal = from.MapIndex(reflect.ValueOf(camelToSnake(fType.Name)))
|
|
}
|
|
}
|
|
|
|
+ default:
|
|
|
|
+ return NewErrFieldType(fieldName, from.Type().String(), to.Type().String(), "unexpected type", -1)
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ if !rVal.IsValid() {
|
|
|
|
+ if required {
|
|
|
|
+ return NewErrorMessage("RequiredField", fType.Name)
|
|
|
|
+ }
|
|
|
|
+ continue
|
|
|
|
+ }
|
|
|
|
+ if rVal.Kind() == reflect.Interface {
|
|
|
|
+ rVal = rVal.Elem()
|
|
}
|
|
}
|
|
- if required && !arrived {
|
|
|
|
- return NewErrFieldRequired(field.Name)
|
|
|
|
|
|
+ iFieldName := fmt.Sprintf("%v.%v", fieldName, fType.Name)
|
|
|
|
+ if err := parseType(rVal, fVal.Addr(), iFieldName); err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func parseMap(from, to reflect.Value, fieldName string) IErrorArgs {
|
|
|
|
+ if to.IsNil() {
|
|
|
|
+ to.Set(reflect.MakeMap(to.Type()))
|
|
|
|
+ }
|
|
|
|
+ switch from.Kind() {
|
|
|
|
+ case reflect.Map:
|
|
|
|
+ for _, key := range from.MapKeys() {
|
|
|
|
+ iFieldName := fmt.Sprintf("%v[%v]", fieldName, key)
|
|
|
|
+ fromVal := from.MapIndex(key)
|
|
|
|
+ toVal := reflect.New(to.Type().Elem()).Elem()
|
|
|
|
+ if err := parseType(fromVal, toVal.Addr(), iFieldName); err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ to.SetMapIndex(key, toVal)
|
|
|
|
+ }
|
|
|
|
+ case reflect.Struct:
|
|
|
|
+ for i := 0; i < from.NumField(); i++ {
|
|
|
|
+ iFieldName := fmt.Sprintf("%v.%v", fieldName, from.Type().Field(i).Name)
|
|
|
|
+ fromVal := from.Field(i)
|
|
|
|
+ toVal := reflect.New(to.Type().Elem()).Elem()
|
|
|
|
+ if err := parseType(fromVal, toVal.Addr(), iFieldName); err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ fName := camelToSnake(from.Type().Field(i).Name)
|
|
|
|
+ to.SetMapIndex(reflect.ValueOf(fName), toVal)
|
|
}
|
|
}
|
|
|
|
+ default:
|
|
|
|
+ return NewErrFieldType(fieldName, from.Type().String(), to.Type().String(), "unexpected type", -1)
|
|
}
|
|
}
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|