|
|
|
|
@ -3,10 +3,10 @@ package copier
|
|
|
|
|
import (
|
|
|
|
|
"database/sql"
|
|
|
|
|
"database/sql/driver"
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"reflect"
|
|
|
|
|
"strings"
|
|
|
|
|
"sync"
|
|
|
|
|
"unicode"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@ -37,15 +37,35 @@ const (
|
|
|
|
|
type Option struct {
|
|
|
|
|
// setting this value to true will ignore copying zero values of all the fields, including bools, as well as a
|
|
|
|
|
// struct having all it's fields set to their zero values respectively (see IsZero() in reflect/value.go)
|
|
|
|
|
IgnoreEmpty bool
|
|
|
|
|
DeepCopy bool
|
|
|
|
|
Converters []TypeConverter
|
|
|
|
|
IgnoreEmpty bool
|
|
|
|
|
CaseSensitive bool
|
|
|
|
|
DeepCopy bool
|
|
|
|
|
Converters []TypeConverter
|
|
|
|
|
// Custom field name mappings to copy values with different names in `fromValue` and `toValue` types.
|
|
|
|
|
// Examples can be found in `copier_field_name_mapping_test.go`.
|
|
|
|
|
FieldNameMapping []FieldNameMapping
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (opt Option) converters() map[converterPair]TypeConverter {
|
|
|
|
|
var converters = map[converterPair]TypeConverter{}
|
|
|
|
|
|
|
|
|
|
// save converters into map for faster lookup
|
|
|
|
|
for i := range opt.Converters {
|
|
|
|
|
pair := converterPair{
|
|
|
|
|
SrcType: reflect.TypeOf(opt.Converters[i].SrcType),
|
|
|
|
|
DstType: reflect.TypeOf(opt.Converters[i].DstType),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
converters[pair] = opt.Converters[i]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return converters
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type TypeConverter struct {
|
|
|
|
|
SrcType interface{}
|
|
|
|
|
DstType interface{}
|
|
|
|
|
Fn func(src interface{}) (interface{}, error)
|
|
|
|
|
Fn func(src interface{}) (dst interface{}, err error)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type converterPair struct {
|
|
|
|
|
@ -53,6 +73,27 @@ type converterPair struct {
|
|
|
|
|
DstType reflect.Type
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (opt Option) fieldNameMapping() map[converterPair]FieldNameMapping {
|
|
|
|
|
var mapping = map[converterPair]FieldNameMapping{}
|
|
|
|
|
|
|
|
|
|
for i := range opt.FieldNameMapping {
|
|
|
|
|
pair := converterPair{
|
|
|
|
|
SrcType: reflect.TypeOf(opt.FieldNameMapping[i].SrcType),
|
|
|
|
|
DstType: reflect.TypeOf(opt.FieldNameMapping[i].DstType),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mapping[pair] = opt.FieldNameMapping[i]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return mapping
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type FieldNameMapping struct {
|
|
|
|
|
SrcType interface{}
|
|
|
|
|
DstType interface{}
|
|
|
|
|
Mapping map[string]string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Tag Flags
|
|
|
|
|
type flags struct {
|
|
|
|
|
BitFlags map[string]uint8
|
|
|
|
|
@ -82,23 +123,10 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error)
|
|
|
|
|
amount = 1
|
|
|
|
|
from = indirect(reflect.ValueOf(fromValue))
|
|
|
|
|
to = indirect(reflect.ValueOf(toValue))
|
|
|
|
|
converters map[converterPair]TypeConverter
|
|
|
|
|
converters = opt.converters()
|
|
|
|
|
mappings = opt.fieldNameMapping()
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// save convertes into map for faster lookup
|
|
|
|
|
for i := range opt.Converters {
|
|
|
|
|
if converters == nil {
|
|
|
|
|
converters = make(map[converterPair]TypeConverter)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pair := converterPair{
|
|
|
|
|
SrcType: reflect.TypeOf(opt.Converters[i].SrcType),
|
|
|
|
|
DstType: reflect.TypeOf(opt.Converters[i].DstType),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
converters[pair] = opt.Converters[i]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !to.CanAddr() {
|
|
|
|
|
return ErrInvalidCopyDestination
|
|
|
|
|
}
|
|
|
|
|
@ -147,7 +175,11 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error)
|
|
|
|
|
|
|
|
|
|
for _, k := range from.MapKeys() {
|
|
|
|
|
toKey := indirect(reflect.New(toType.Key()))
|
|
|
|
|
if !set(toKey, k, opt.DeepCopy, converters) {
|
|
|
|
|
isSet, err := set(toKey, k, opt.DeepCopy, converters)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if !isSet {
|
|
|
|
|
return fmt.Errorf("%w map, old key: %v, new key: %v", ErrNotSupported, k.Type(), toType.Key())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -156,7 +188,11 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error)
|
|
|
|
|
elemType, _ = indirectType(elemType)
|
|
|
|
|
}
|
|
|
|
|
toValue := indirect(reflect.New(elemType))
|
|
|
|
|
if !set(toValue, from.MapIndex(k), opt.DeepCopy, converters) {
|
|
|
|
|
isSet, err = set(toValue, from.MapIndex(k), opt.DeepCopy, converters)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if !isSet {
|
|
|
|
|
if err = copier(toValue.Addr().Interface(), from.MapIndex(k).Interface(), opt); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
@ -174,26 +210,30 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if from.Kind() == reflect.Slice && to.Kind() == reflect.Slice && fromType.ConvertibleTo(toType) {
|
|
|
|
|
if from.Kind() == reflect.Slice && to.Kind() == reflect.Slice {
|
|
|
|
|
if to.IsNil() {
|
|
|
|
|
slice := reflect.MakeSlice(reflect.SliceOf(to.Type().Elem()), from.Len(), from.Cap())
|
|
|
|
|
to.Set(slice)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i := 0; i < from.Len(); i++ {
|
|
|
|
|
if to.Len() < i+1 {
|
|
|
|
|
to.Set(reflect.Append(to, reflect.New(to.Type().Elem()).Elem()))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !set(to.Index(i), from.Index(i), opt.DeepCopy, converters) {
|
|
|
|
|
// ignore error while copy slice element
|
|
|
|
|
err = copier(to.Index(i).Addr().Interface(), from.Index(i).Interface(), opt)
|
|
|
|
|
if fromType.ConvertibleTo(toType) {
|
|
|
|
|
for i := 0; i < from.Len(); i++ {
|
|
|
|
|
if to.Len() < i+1 {
|
|
|
|
|
to.Set(reflect.Append(to, reflect.New(to.Type().Elem()).Elem()))
|
|
|
|
|
}
|
|
|
|
|
isSet, err := set(to.Index(i), from.Index(i), opt.DeepCopy, converters)
|
|
|
|
|
if err != nil {
|
|
|
|
|
continue
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if !isSet {
|
|
|
|
|
// ignore error while copy slice element
|
|
|
|
|
err = copier(to.Index(i).Addr().Interface(), from.Index(i).Interface(), opt)
|
|
|
|
|
if err != nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if fromType.Kind() != reflect.Struct || toType.Kind() != reflect.Struct {
|
|
|
|
|
@ -201,6 +241,13 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(converters) > 0 {
|
|
|
|
|
if ok, e := set(to, from, opt.DeepCopy, converters); e == nil && ok {
|
|
|
|
|
// converter supported
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if from.Kind() == reflect.Slice || to.Kind() == reflect.Slice {
|
|
|
|
|
isSlice = true
|
|
|
|
|
if from.Kind() == reflect.Slice {
|
|
|
|
|
@ -225,6 +272,27 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error)
|
|
|
|
|
dest = indirect(to)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(converters) > 0 {
|
|
|
|
|
if ok, e := set(dest, source, opt.DeepCopy, converters); e == nil && ok {
|
|
|
|
|
if isSlice {
|
|
|
|
|
// FIXME: maybe should check the other types?
|
|
|
|
|
if to.Type().Elem().Kind() == reflect.Ptr {
|
|
|
|
|
to.Index(i).Set(dest.Addr())
|
|
|
|
|
} else {
|
|
|
|
|
if to.Len() < i+1 {
|
|
|
|
|
reflect.Append(to, dest)
|
|
|
|
|
} else {
|
|
|
|
|
to.Index(i).Set(dest)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
to.Set(dest)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
destKind := dest.Kind()
|
|
|
|
|
initDest := false
|
|
|
|
|
if destKind == reflect.Interface {
|
|
|
|
|
@ -248,19 +316,22 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error)
|
|
|
|
|
name := field.Name
|
|
|
|
|
|
|
|
|
|
// Get bit flags for field
|
|
|
|
|
fieldFlags, _ := flgs.BitFlags[name]
|
|
|
|
|
fieldFlags := flgs.BitFlags[name]
|
|
|
|
|
|
|
|
|
|
// Check if we should ignore copying
|
|
|
|
|
if (fieldFlags & tagIgnore) != 0 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
srcFieldName, destFieldName := getFieldName(name, flgs)
|
|
|
|
|
if fromField := source.FieldByName(srcFieldName); fromField.IsValid() && !shouldIgnore(fromField, opt.IgnoreEmpty) {
|
|
|
|
|
fieldNamesMapping := getFieldNamesMapping(mappings, fromType, toType)
|
|
|
|
|
|
|
|
|
|
srcFieldName, destFieldName := getFieldName(name, flgs, fieldNamesMapping)
|
|
|
|
|
if fromField := fieldByNameOrZeroValue(source, srcFieldName); fromField.IsValid() && !shouldIgnore(fromField, opt.IgnoreEmpty) {
|
|
|
|
|
// process for nested anonymous field
|
|
|
|
|
destFieldNotSet := false
|
|
|
|
|
if f, ok := dest.Type().FieldByName(destFieldName); ok {
|
|
|
|
|
for idx := range f.Index {
|
|
|
|
|
// only initialize parent embedded struct pointer in the path
|
|
|
|
|
for idx := range f.Index[:len(f.Index)-1] {
|
|
|
|
|
destField := dest.FieldByIndex(f.Index[:idx+1])
|
|
|
|
|
|
|
|
|
|
if destField.Kind() != reflect.Ptr {
|
|
|
|
|
@ -285,10 +356,14 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error)
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
toField := dest.FieldByName(destFieldName)
|
|
|
|
|
toField := fieldByName(dest, destFieldName, opt.CaseSensitive)
|
|
|
|
|
if toField.IsValid() {
|
|
|
|
|
if toField.CanSet() {
|
|
|
|
|
if !set(toField, fromField, opt.DeepCopy, converters) {
|
|
|
|
|
isSet, err := set(toField, fromField, opt.DeepCopy, converters)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if !isSet {
|
|
|
|
|
if err := copier(toField.Addr().Interface(), fromField.Interface(), opt); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
@ -317,7 +392,7 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error)
|
|
|
|
|
// Copy from from method to dest field
|
|
|
|
|
for _, field := range deepFields(toType) {
|
|
|
|
|
name := field.Name
|
|
|
|
|
srcFieldName, destFieldName := getFieldName(name, flgs)
|
|
|
|
|
srcFieldName, destFieldName := getFieldName(name, flgs, getFieldNamesMapping(mappings, fromType, toType))
|
|
|
|
|
|
|
|
|
|
var fromMethod reflect.Value
|
|
|
|
|
if source.CanAddr() {
|
|
|
|
|
@ -327,7 +402,7 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if fromMethod.IsValid() && fromMethod.Type().NumIn() == 0 && fromMethod.Type().NumOut() == 1 && !shouldIgnore(fromMethod, opt.IgnoreEmpty) {
|
|
|
|
|
if toField := dest.FieldByName(destFieldName); toField.IsValid() && toField.CanSet() {
|
|
|
|
|
if toField := fieldByName(dest, destFieldName, opt.CaseSensitive); toField.IsValid() && toField.CanSet() {
|
|
|
|
|
values := fromMethod.Call([]reflect.Value{})
|
|
|
|
|
if len(values) >= 1 {
|
|
|
|
|
set(toField, values[0], opt.DeepCopy, converters)
|
|
|
|
|
@ -342,7 +417,11 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error)
|
|
|
|
|
if to.Len() < i+1 {
|
|
|
|
|
to.Set(reflect.Append(to, dest.Addr()))
|
|
|
|
|
} else {
|
|
|
|
|
if !set(to.Index(i), dest.Addr(), opt.DeepCopy, converters) {
|
|
|
|
|
isSet, err := set(to.Index(i), dest.Addr(), opt.DeepCopy, converters)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if !isSet {
|
|
|
|
|
// ignore error while copy slice element
|
|
|
|
|
err = copier(to.Index(i).Addr().Interface(), dest.Addr().Interface(), opt)
|
|
|
|
|
if err != nil {
|
|
|
|
|
@ -354,7 +433,11 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error)
|
|
|
|
|
if to.Len() < i+1 {
|
|
|
|
|
to.Set(reflect.Append(to, dest))
|
|
|
|
|
} else {
|
|
|
|
|
if !set(to.Index(i), dest, opt.DeepCopy, converters) {
|
|
|
|
|
isSet, err := set(to.Index(i), dest, opt.DeepCopy, converters)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if !isSet {
|
|
|
|
|
// ignore error while copy slice element
|
|
|
|
|
err = copier(to.Index(i).Addr().Interface(), dest.Interface(), opt)
|
|
|
|
|
if err != nil {
|
|
|
|
|
@ -373,6 +456,31 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getFieldNamesMapping(mappings map[converterPair]FieldNameMapping, fromType reflect.Type, toType reflect.Type) map[string]string {
|
|
|
|
|
var fieldNamesMapping map[string]string
|
|
|
|
|
|
|
|
|
|
if len(mappings) > 0 {
|
|
|
|
|
pair := converterPair{
|
|
|
|
|
SrcType: fromType,
|
|
|
|
|
DstType: toType,
|
|
|
|
|
}
|
|
|
|
|
if v, ok := mappings[pair]; ok {
|
|
|
|
|
fieldNamesMapping = v.Mapping
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return fieldNamesMapping
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func fieldByNameOrZeroValue(source reflect.Value, fieldName string) (value reflect.Value) {
|
|
|
|
|
defer func() {
|
|
|
|
|
if err := recover(); err != nil {
|
|
|
|
|
value = reflect.Value{}
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
return source.FieldByName(fieldName)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func copyUnexportedStructFields(to, from reflect.Value) {
|
|
|
|
|
if from.Kind() != reflect.Struct || to.Kind() != reflect.Struct || !from.Type().AssignableTo(to.Type()) {
|
|
|
|
|
return
|
|
|
|
|
@ -392,14 +500,20 @@ func copyUnexportedStructFields(to, from reflect.Value) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func shouldIgnore(v reflect.Value, ignoreEmpty bool) bool {
|
|
|
|
|
if !ignoreEmpty {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return v.IsZero()
|
|
|
|
|
return ignoreEmpty && v.IsZero()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var deepFieldsLock sync.RWMutex
|
|
|
|
|
var deepFieldsMap = make(map[reflect.Type][]reflect.StructField)
|
|
|
|
|
|
|
|
|
|
func deepFields(reflectType reflect.Type) []reflect.StructField {
|
|
|
|
|
deepFieldsLock.RLock()
|
|
|
|
|
cache, ok := deepFieldsMap[reflectType]
|
|
|
|
|
deepFieldsLock.RUnlock()
|
|
|
|
|
if ok {
|
|
|
|
|
return cache
|
|
|
|
|
}
|
|
|
|
|
var res []reflect.StructField
|
|
|
|
|
if reflectType, _ = indirectType(reflectType); reflectType.Kind() == reflect.Struct {
|
|
|
|
|
fields := make([]reflect.StructField, 0, reflectType.NumField())
|
|
|
|
|
|
|
|
|
|
@ -416,11 +530,13 @@ func deepFields(reflectType reflect.Type) []reflect.StructField {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return fields
|
|
|
|
|
res = fields
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
deepFieldsLock.Lock()
|
|
|
|
|
deepFieldsMap[reflectType] = res
|
|
|
|
|
deepFieldsLock.Unlock()
|
|
|
|
|
return res
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func indirect(reflectValue reflect.Value) reflect.Value {
|
|
|
|
|
@ -438,98 +554,101 @@ func indirectType(reflectType reflect.Type) (_ reflect.Type, isPtr bool) {
|
|
|
|
|
return reflectType, isPtr
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func set(to, from reflect.Value, deepCopy bool, converters map[converterPair]TypeConverter) bool {
|
|
|
|
|
if from.IsValid() {
|
|
|
|
|
if ok, err := lookupAndCopyWithConverter(to, from, converters); err != nil {
|
|
|
|
|
return false
|
|
|
|
|
} else if ok {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
func set(to, from reflect.Value, deepCopy bool, converters map[converterPair]TypeConverter) (bool, error) {
|
|
|
|
|
if !from.IsValid() {
|
|
|
|
|
return true, nil
|
|
|
|
|
}
|
|
|
|
|
if ok, err := lookupAndCopyWithConverter(to, from, converters); err != nil {
|
|
|
|
|
return false, err
|
|
|
|
|
} else if ok {
|
|
|
|
|
return true, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if to.Kind() == reflect.Ptr {
|
|
|
|
|
// set `to` to nil if from is nil
|
|
|
|
|
if from.Kind() == reflect.Ptr && from.IsNil() {
|
|
|
|
|
to.Set(reflect.Zero(to.Type()))
|
|
|
|
|
return true
|
|
|
|
|
} else if to.IsNil() {
|
|
|
|
|
// `from` -> `to`
|
|
|
|
|
// sql.NullString -> *string
|
|
|
|
|
if fromValuer, ok := driverValuer(from); ok {
|
|
|
|
|
v, err := fromValuer.Value()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
// if `from` is not valid do nothing with `to`
|
|
|
|
|
if v == nil {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// allocate new `to` variable with default value (eg. *string -> new(string))
|
|
|
|
|
to.Set(reflect.New(to.Type().Elem()))
|
|
|
|
|
}
|
|
|
|
|
// depointer `to`
|
|
|
|
|
to = to.Elem()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if deepCopy {
|
|
|
|
|
toKind := to.Kind()
|
|
|
|
|
if toKind == reflect.Interface && to.IsNil() {
|
|
|
|
|
if reflect.TypeOf(from.Interface()) != nil {
|
|
|
|
|
to.Set(reflect.New(reflect.TypeOf(from.Interface())).Elem())
|
|
|
|
|
toKind = reflect.TypeOf(to.Interface()).Kind()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if from.Kind() == reflect.Ptr && from.IsNil() {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
if toKind == reflect.Struct || toKind == reflect.Map || toKind == reflect.Slice {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if from.Type().ConvertibleTo(to.Type()) {
|
|
|
|
|
to.Set(from.Convert(to.Type()))
|
|
|
|
|
} else if toScanner, ok := to.Addr().Interface().(sql.Scanner); ok {
|
|
|
|
|
// `from` -> `to`
|
|
|
|
|
// *string -> sql.NullString
|
|
|
|
|
if from.Kind() == reflect.Ptr {
|
|
|
|
|
// if `from` is nil do nothing with `to`
|
|
|
|
|
if from.IsNil() {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
// depointer `from`
|
|
|
|
|
from = indirect(from)
|
|
|
|
|
}
|
|
|
|
|
// `from` -> `to`
|
|
|
|
|
// string -> sql.NullString
|
|
|
|
|
// set `to` by invoking method Scan(`from`)
|
|
|
|
|
err := toScanner.Scan(from.Interface())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
} else if fromValuer, ok := driverValuer(from); ok {
|
|
|
|
|
if to.Kind() == reflect.Ptr {
|
|
|
|
|
// set `to` to nil if from is nil
|
|
|
|
|
if from.Kind() == reflect.Ptr && from.IsNil() {
|
|
|
|
|
to.Set(reflect.Zero(to.Type()))
|
|
|
|
|
return true, nil
|
|
|
|
|
} else if to.IsNil() {
|
|
|
|
|
// `from` -> `to`
|
|
|
|
|
// sql.NullString -> string
|
|
|
|
|
v, err := fromValuer.Value()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return false
|
|
|
|
|
// sql.NullString -> *string
|
|
|
|
|
if fromValuer, ok := driverValuer(from); ok {
|
|
|
|
|
v, err := fromValuer.Value()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return true, nil
|
|
|
|
|
}
|
|
|
|
|
// if `from` is not valid do nothing with `to`
|
|
|
|
|
if v == nil {
|
|
|
|
|
return true, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// if `from` is not valid do nothing with `to`
|
|
|
|
|
if v == nil {
|
|
|
|
|
return true
|
|
|
|
|
// allocate new `to` variable with default value (eg. *string -> new(string))
|
|
|
|
|
to.Set(reflect.New(to.Type().Elem()))
|
|
|
|
|
}
|
|
|
|
|
// depointer `to`
|
|
|
|
|
to = to.Elem()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if deepCopy {
|
|
|
|
|
toKind := to.Kind()
|
|
|
|
|
if toKind == reflect.Interface && to.IsNil() {
|
|
|
|
|
if reflect.TypeOf(from.Interface()) != nil {
|
|
|
|
|
to.Set(reflect.New(reflect.TypeOf(from.Interface())).Elem())
|
|
|
|
|
toKind = reflect.TypeOf(to.Interface()).Kind()
|
|
|
|
|
}
|
|
|
|
|
rv := reflect.ValueOf(v)
|
|
|
|
|
if rv.Type().AssignableTo(to.Type()) {
|
|
|
|
|
to.Set(rv)
|
|
|
|
|
}
|
|
|
|
|
} else if from.Kind() == reflect.Ptr {
|
|
|
|
|
return set(to, from.Elem(), deepCopy, converters)
|
|
|
|
|
} else {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
if from.Kind() == reflect.Ptr && from.IsNil() {
|
|
|
|
|
return true, nil
|
|
|
|
|
}
|
|
|
|
|
if _, ok := to.Addr().Interface().(sql.Scanner); !ok && (toKind == reflect.Struct || toKind == reflect.Map || toKind == reflect.Slice) {
|
|
|
|
|
return false, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
if from.Type().ConvertibleTo(to.Type()) {
|
|
|
|
|
to.Set(from.Convert(to.Type()))
|
|
|
|
|
} else if toScanner, ok := to.Addr().Interface().(sql.Scanner); ok {
|
|
|
|
|
// `from` -> `to`
|
|
|
|
|
// *string -> sql.NullString
|
|
|
|
|
if from.Kind() == reflect.Ptr {
|
|
|
|
|
// if `from` is nil do nothing with `to`
|
|
|
|
|
if from.IsNil() {
|
|
|
|
|
return true, nil
|
|
|
|
|
}
|
|
|
|
|
// depointer `from`
|
|
|
|
|
from = indirect(from)
|
|
|
|
|
}
|
|
|
|
|
// `from` -> `to`
|
|
|
|
|
// string -> sql.NullString
|
|
|
|
|
// set `to` by invoking method Scan(`from`)
|
|
|
|
|
err := toScanner.Scan(from.Interface())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return false, nil
|
|
|
|
|
}
|
|
|
|
|
} else if fromValuer, ok := driverValuer(from); ok {
|
|
|
|
|
// `from` -> `to`
|
|
|
|
|
// sql.NullString -> string
|
|
|
|
|
v, err := fromValuer.Value()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return false, nil
|
|
|
|
|
}
|
|
|
|
|
// if `from` is not valid do nothing with `to`
|
|
|
|
|
if v == nil {
|
|
|
|
|
return true, nil
|
|
|
|
|
}
|
|
|
|
|
rv := reflect.ValueOf(v)
|
|
|
|
|
if rv.Type().AssignableTo(to.Type()) {
|
|
|
|
|
to.Set(rv)
|
|
|
|
|
} else if to.CanSet() && rv.Type().ConvertibleTo(to.Type()) {
|
|
|
|
|
to.Set(rv.Convert(to.Type()))
|
|
|
|
|
}
|
|
|
|
|
} else if from.Kind() == reflect.Ptr {
|
|
|
|
|
return set(to, from.Elem(), deepCopy, converters)
|
|
|
|
|
} else {
|
|
|
|
|
return false, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// lookupAndCopyWithConverter looks up the type pair, on success the TypeConverter Fn func is called to copy src to dst field.
|
|
|
|
|
@ -541,7 +660,6 @@ func lookupAndCopyWithConverter(to, from reflect.Value, converters map[converter
|
|
|
|
|
|
|
|
|
|
if cnv, ok := converters[pair]; ok {
|
|
|
|
|
result, err := cnv.Fn(from.Interface())
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return false, err
|
|
|
|
|
}
|
|
|
|
|
@ -574,7 +692,7 @@ func parseTags(tag string) (flg uint8, name string, err error) {
|
|
|
|
|
if unicode.IsUpper([]rune(t)[0]) {
|
|
|
|
|
name = strings.TrimSpace(t)
|
|
|
|
|
} else {
|
|
|
|
|
err = errors.New("copier field name tag must be start upper case")
|
|
|
|
|
err = ErrFieldNameTagStartNotUpperCase
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -651,8 +769,14 @@ func checkBitFlags(flagsList map[string]uint8) (err error) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getFieldName(fieldName string, flgs flags) (srcFieldName string, destFieldName string) {
|
|
|
|
|
func getFieldName(fieldName string, flgs flags, fieldNameMapping map[string]string) (srcFieldName string, destFieldName string) {
|
|
|
|
|
// get dest field name
|
|
|
|
|
if name, ok := fieldNameMapping[fieldName]; ok {
|
|
|
|
|
srcFieldName = fieldName
|
|
|
|
|
destFieldName = name
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if srcTagName, ok := flgs.SrcNames.FieldNameToTag[fieldName]; ok {
|
|
|
|
|
destFieldName = srcTagName
|
|
|
|
|
if destTagName, ok := flgs.DestNames.TagToFieldName[srcTagName]; ok {
|
|
|
|
|
@ -686,7 +810,6 @@ func getFieldName(fieldName string, flgs flags) (srcFieldName string, destFieldN
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func driverValuer(v reflect.Value) (i driver.Valuer, ok bool) {
|
|
|
|
|
|
|
|
|
|
if !v.CanAddr() {
|
|
|
|
|
i, ok = v.Interface().(driver.Valuer)
|
|
|
|
|
return
|
|
|
|
|
@ -695,3 +818,11 @@ func driverValuer(v reflect.Value) (i driver.Valuer, ok bool) {
|
|
|
|
|
i, ok = v.Addr().Interface().(driver.Valuer)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func fieldByName(v reflect.Value, name string, caseSensitive bool) reflect.Value {
|
|
|
|
|
if caseSensitive {
|
|
|
|
|
return v.FieldByName(name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return v.FieldByNameFunc(func(n string) bool { return strings.EqualFold(n, name) })
|
|
|
|
|
}
|
|
|
|
|
|