123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- package rest
- import (
- "fmt"
- "reflect"
- "strings"
- "git.ali33.ru/fcg-xvii/go-tools/json"
- )
- // IRestConverter реализует пользовательский пасер из значения, полученного из запроса (см. пример из time.go)
- type IRestConverter interface {
- // RestFrom реализeт конвертацию из данных запроса в объект
- RestFrom(any) IErrorArgs
- }
- func parseVal(from, to reflect.Value, fieldName string) IErrorArgs {
- if from.Kind() == reflect.Interface {
- from = from.Elem()
- }
- if !from.CanConvert(to.Type()) {
- return NewError(
- "FieldType",
- json.Map{
- "field": fieldName,
- "expected": to.Type().String(),
- "received": from.Type().String(),
- },
- )
- }
- 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:
- if from.Kind() != reflect.Invalid {
- 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)
- }
- }
- func realValue(val reflect.Value) reflect.Value {
- if val.Kind() == reflect.Ptr || val.Kind() == reflect.Interface {
- if val.IsNil() && val.Kind() == reflect.Ptr {
- val.Set(reflect.New(val.Type().Elem()))
- }
- // check
- cval := val.Elem()
- if cval.Kind() == reflect.Invalid {
- return val
- }
- val = cval
- if val.Kind() == reflect.Ptr || val.Kind() == reflect.Interface {
- return realValue(cval)
- }
- }
- return val
- }
- 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))
- }
- return nil
- }
- func parseStruct(from, to reflect.Value, fieldName string) IErrorArgs {
- if f, check := to.Interface().(IRestConverter); check {
- return f.RestFrom(from.Interface())
- }
- if to.CanAddr() {
- if f, check := to.Addr().Interface().(IRestConverter); check {
- return f.RestFrom(from.Interface())
- }
- }
- 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 {
- required = strings.Contains(tag, "required")
- }
- var rVal reflect.Value
- switch from.Kind() {
- case reflect.Struct:
- if rVal = from.FieldByName(fType.Name); !rVal.IsValid() {
- rVal = from.FieldByName(camelToSnake(fType.Name))
- }
- case reflect.Map:
- if rVal = from.MapIndex(reflect.ValueOf(fType.Name)); !rVal.IsValid() {
- rVal = from.MapIndex(reflect.ValueOf(camelToSnake(fType.Name)))
- }
- default:
- return NewError(
- "FieldType",
- json.Map{
- "field": fieldName,
- "expected": to.Type().String(),
- "received": from.Type().String(),
- },
- )
- }
- if !rVal.IsValid() {
- if required {
- return ErrorMessage("RequiredField", fType.Name)
- }
- continue
- }
- if rVal.Kind() == reflect.Interface {
- rVal = rVal.Elem()
- }
- 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 NewError(
- "FieldType",
- json.Map{
- "field": fieldName,
- "expected": to.Type().String(),
- "received": from.Type().String(),
- },
- )
- }
- return nil
- }
- // Serialize преобразует словарь from в объект произвольного типа
- func Serialize(from json.Map, to any) IErrorArgs {
- elemTo := reflect.ValueOf(to).Elem()
- return parseType(
- reflect.ValueOf(&from),
- elemTo,
- "",
- )
- }
|