You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
266 lines
6.6 KiB
Go
266 lines
6.6 KiB
Go
package nmapper
|
|
|
|
import (
|
|
"errors"
|
|
"git.noahlan.cn/noahlan/ntool/internal/convert"
|
|
"git.noahlan.cn/noahlan/ntool/nmap"
|
|
"git.noahlan.cn/noahlan/ntool/nmapper/wrapper"
|
|
"git.noahlan.cn/noahlan/ntool/nmath"
|
|
"git.noahlan.cn/noahlan/ntool/nreflect"
|
|
"git.noahlan.cn/noahlan/ntool/nstr"
|
|
"git.noahlan.cn/noahlan/ntool/nstruct"
|
|
"reflect"
|
|
"time"
|
|
)
|
|
|
|
type MapperObject struct {
|
|
ZeroValue reflect.Value
|
|
|
|
typeWrappers []wrapper.TypeWrapper
|
|
timeType reflect.Type // 时间类型
|
|
enabledTypeChecking bool // 是否开启类型检查
|
|
|
|
customTagName string // 自定义Tag名称
|
|
}
|
|
|
|
func NewMapper(opts ...Option) *MapperObject {
|
|
tmp := &MapperObject{
|
|
ZeroValue: reflect.Value{},
|
|
timeType: reflect.TypeOf(time.Time{}),
|
|
typeWrappers: make([]wrapper.TypeWrapper, 0),
|
|
enabledTypeChecking: false,
|
|
customTagName: "",
|
|
}
|
|
|
|
for _, opt := range opts {
|
|
opt(tmp)
|
|
}
|
|
|
|
return tmp
|
|
}
|
|
|
|
func (m *MapperObject) SetTypeWrapper(wrappers ...wrapper.TypeWrapper) {
|
|
m.typeWrappers = wrappers
|
|
}
|
|
|
|
func (m *MapperObject) RegisterTypeWrapper(wrappers ...wrapper.TypeWrapper) {
|
|
m.typeWrappers = append(m.typeWrappers, wrappers...)
|
|
}
|
|
|
|
// MapperObject 将src转换为dst
|
|
// src为struct类型, dst为map类型 map[string]any 或者 map[string]string
|
|
func (m *MapperObject) Mapper(dst any, src any) error {
|
|
srcElem := reflect.ValueOf(src)
|
|
srcElem = nreflect.Indirect(srcElem)
|
|
|
|
dstElem := reflect.ValueOf(dst)
|
|
dstElem = nreflect.Indirect(dstElem)
|
|
|
|
if srcElem == m.ZeroValue {
|
|
return errors.New("src is not legal value")
|
|
}
|
|
if dstElem == m.ZeroValue {
|
|
return errors.New("dst is not legal value")
|
|
}
|
|
return m.elemMapper(dstElem, srcElem)
|
|
}
|
|
|
|
func (m *MapperObject) elemMapper(dstElem, srcElem reflect.Value) error {
|
|
//if dstElem.Type().Kind() != reflect.Map {
|
|
// return errors.New(fmt.Sprintf("dst is not legal value, need map but got %s", dstElem.Type().Kind().String()))
|
|
//}
|
|
if dstElem.Type().Kind() == reflect.Map {
|
|
m.elemToMap(dstElem, srcElem)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (m *MapperObject) elemToMap(dstElem, srcElem reflect.Value) {
|
|
for i := 0; i < srcElem.NumField(); i++ {
|
|
srcFieldInfo := srcElem.Field(i)
|
|
// tag
|
|
name, tagMap := m.getFieldNameAndMap(srcElem, i)
|
|
if name == "" {
|
|
continue
|
|
}
|
|
|
|
valElem := srcFieldInfo
|
|
for _, w := range m.typeWrappers {
|
|
if w.Supported(srcFieldInfo) {
|
|
valElem = w.Wrap(srcFieldInfo, i, tagMap)
|
|
break
|
|
}
|
|
}
|
|
dstElem.SetMapIndex(reflect.ValueOf(name), valElem)
|
|
}
|
|
}
|
|
|
|
func (m *MapperObject) ParseMap(dst any, src map[string]any) error {
|
|
dstElem := reflect.ValueOf(dst)
|
|
dstElem = nreflect.Indirect(dstElem)
|
|
|
|
if dstElem == m.ZeroValue {
|
|
return errors.New("dst is not legal value")
|
|
}
|
|
|
|
for i := 0; i < dstElem.NumField(); i++ {
|
|
dstFieldInfo := dstElem.Field(i)
|
|
name, tagMap := m.getFieldNameAndMap(dstElem, i)
|
|
if name == "" {
|
|
continue
|
|
}
|
|
m.setFieldValue(dstFieldInfo, tagMap, src[name])
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *MapperObject) ParseStrMap(dst any, src map[string]string) error {
|
|
dstElem := reflect.ValueOf(dst)
|
|
dstElem = nreflect.Indirect(dstElem)
|
|
|
|
if dstElem == m.ZeroValue {
|
|
return errors.New("dst is not legal value")
|
|
}
|
|
|
|
for i := 0; i < dstElem.NumField(); i++ {
|
|
dstFieldInfo := dstElem.Field(i)
|
|
name, tagMap := m.getFieldNameAndMap(dstElem, i)
|
|
if name == "" {
|
|
continue
|
|
}
|
|
m.setFieldValue(dstFieldInfo, tagMap, src[name])
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *MapperObject) setFieldValue(fieldElem reflect.Value, tagMap nmap.SMap, value any) {
|
|
fieldKind := fieldElem.Type().Kind()
|
|
|
|
switch fieldKind {
|
|
case reflect.Bool:
|
|
if value == nil {
|
|
fieldElem.SetBool(false)
|
|
} else if v, ok := value.(bool); ok {
|
|
fieldElem.SetBool(v)
|
|
} else {
|
|
b, _ := convert.StrToBool(nstr.SafeString(value))
|
|
fieldElem.SetBool(b)
|
|
}
|
|
case reflect.String:
|
|
if value == nil {
|
|
fieldElem.SetString("")
|
|
} else {
|
|
fieldElem.SetString(nstr.SafeString(value))
|
|
}
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
if value == nil {
|
|
fieldElem.SetInt(0)
|
|
} else {
|
|
val := reflect.ValueOf(value)
|
|
switch val.Kind() {
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
fieldElem.SetInt(val.Int())
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
fieldElem.SetInt(int64(val.Uint()))
|
|
default:
|
|
v, _ := nmath.ToInt64(value)
|
|
fieldElem.SetInt(v)
|
|
}
|
|
}
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
if value == nil {
|
|
fieldElem.SetUint(0)
|
|
} else {
|
|
val := reflect.ValueOf(value)
|
|
switch val.Kind() {
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
fieldElem.SetUint(uint64(val.Int()))
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
fieldElem.SetUint(val.Uint())
|
|
default:
|
|
v, _ := nmath.ToUint(value)
|
|
fieldElem.SetUint(v)
|
|
}
|
|
}
|
|
case reflect.Float64, reflect.Float32:
|
|
if value == nil {
|
|
fieldElem.SetFloat(0)
|
|
} else {
|
|
val := reflect.ValueOf(value)
|
|
switch val.Kind() {
|
|
case reflect.Float64:
|
|
fieldElem.SetFloat(val.Float())
|
|
default:
|
|
fieldElem.SetFloat(nmath.QuietFloat(value))
|
|
}
|
|
}
|
|
case reflect.Struct:
|
|
if value == nil {
|
|
fieldElem.Set(reflect.Zero(fieldElem.Type()))
|
|
} else {
|
|
for _, w := range m.typeWrappers {
|
|
if w.Supported(fieldElem) {
|
|
w.Set(fieldElem, value, tagMap)
|
|
break
|
|
}
|
|
}
|
|
if reflect.ValueOf(value).Type() == fieldElem.Type() {
|
|
fieldElem.Set(reflect.ValueOf(value))
|
|
}
|
|
}
|
|
default:
|
|
if !fieldElem.IsValid() {
|
|
fieldElem.Set(reflect.Zero(fieldElem.Type()))
|
|
return
|
|
}
|
|
// Type check
|
|
if reflect.ValueOf(value).Type() == fieldElem.Type() {
|
|
fieldElem.Set(reflect.ValueOf(value))
|
|
}
|
|
}
|
|
}
|
|
|
|
// getFieldNameAndMap 获取字段名称和tagMap
|
|
func (m *MapperObject) getFieldNameAndMap(objElem reflect.Value, index int) (string, nmap.SMap) {
|
|
field := objElem.Type().Field(index)
|
|
// 1. custom
|
|
tagVal, ok := field.Tag.Lookup(m.customTagName)
|
|
if ok && tagVal != "" {
|
|
sMap, _ := nstruct.ParseTagValueDefault(field.Name, tagVal)
|
|
name := sMap.Get("name")
|
|
if name != "" {
|
|
return name, sMap
|
|
} else {
|
|
// tag "-"
|
|
return "", nil
|
|
}
|
|
}
|
|
// 2. mapper
|
|
tagVal, ok = field.Tag.Lookup(mapperTagKey)
|
|
if ok && tagVal != "" {
|
|
sMap, _ := nstruct.ParseTagValueDefault(field.Name, tagVal)
|
|
name := sMap.Get("name")
|
|
if name != "" {
|
|
return name, sMap
|
|
} else {
|
|
// tag "-"
|
|
return "", nil
|
|
}
|
|
}
|
|
|
|
// 3. json
|
|
tagVal, ok = field.Tag.Lookup(jsonTagKey)
|
|
if ok && tagVal != "" {
|
|
sMap, _ := nstruct.ParseTagValueDefault(field.Name, tagVal)
|
|
name := sMap.Get("name")
|
|
if name != "" {
|
|
return name, sMap
|
|
} else {
|
|
// tag "-"
|
|
return "", nil
|
|
}
|
|
}
|
|
return field.Name, nmap.SMap{"name": field.Name}
|
|
}
|