Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add TypeConverter function with field names. #138

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 23 additions & 15 deletions copier.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,10 @@ type Option struct {
}

type TypeConverter struct {
SrcType interface{}
DstType interface{}
Fn func(src interface{}) (dst interface{}, err error)
SrcType interface{}
DstType interface{}
Fn func(src interface{}) (dst interface{}, err error)
FnWithName func(fromFieldName, toFieldName string, src interface{}) (dst interface{}, err error)
}

type converterPair struct {
Expand Down Expand Up @@ -146,7 +147,7 @@ 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) {
if !set(k.String(), "", toKey, k, opt.DeepCopy, converters) {
return fmt.Errorf("%w map, old key: %v, new key: %v", ErrNotSupported, k.Type(), toType.Key())
}

Expand All @@ -155,7 +156,7 @@ 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) {
if !set(k.String(), "", toValue, from.MapIndex(k), opt.DeepCopy, converters) {
if err = copier(toValue.Addr().Interface(), from.MapIndex(k).Interface(), opt); err != nil {
return err
}
Expand Down Expand Up @@ -184,7 +185,7 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error)
to.Set(reflect.Append(to, reflect.New(to.Type().Elem()).Elem()))
}

if !set(to.Index(i), from.Index(i), opt.DeepCopy, converters) {
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 err != nil {
Expand Down Expand Up @@ -287,7 +288,7 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error)
toField := dest.FieldByName(destFieldName)
if toField.IsValid() {
if toField.CanSet() {
if !set(toField, fromField, opt.DeepCopy, converters) {
if !set(destFieldName, srcFieldName, toField, fromField, opt.DeepCopy, converters) {
if err := copier(toField.Addr().Interface(), fromField.Interface(), opt); err != nil {
return err
}
Expand Down Expand Up @@ -329,7 +330,7 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error)
if toField := dest.FieldByName(destFieldName); toField.IsValid() && toField.CanSet() {
values := fromMethod.Call([]reflect.Value{})
if len(values) >= 1 {
set(toField, values[0], opt.DeepCopy, converters)
set(destFieldName, srcFieldName, toField, values[0], opt.DeepCopy, converters)
}
}
}
Expand All @@ -341,7 +342,7 @@ 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) {
if !set("", "", to.Index(i), dest.Addr(), opt.DeepCopy, converters) {
// ignore error while copy slice element
err = copier(to.Index(i).Addr().Interface(), dest.Addr().Interface(), opt)
if err != nil {
Expand All @@ -353,7 +354,7 @@ 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) {
if !set("", "", to.Index(i), dest, opt.DeepCopy, converters) {
// ignore error while copy slice element
err = copier(to.Index(i).Addr().Interface(), dest.Interface(), opt)
if err != nil {
Expand Down Expand Up @@ -433,11 +434,11 @@ 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 {
func set(toFieldName, fromFieldName string, to, from reflect.Value, deepCopy bool, converters map[converterPair]TypeConverter) bool {
if !from.IsValid() {
return true
}
if ok, err := lookupAndCopyWithConverter(to, from, converters); err != nil {
if ok, err := lookupAndCopyWithConverter(toFieldName, fromFieldName, to, from, converters); err != nil {
return false
} else if ok {
return true
Expand Down Expand Up @@ -520,7 +521,7 @@ func set(to, from reflect.Value, deepCopy bool, converters map[converterPair]Typ
to.Set(rv)
}
} else if from.Kind() == reflect.Ptr {
return set(to, from.Elem(), deepCopy, converters)
return set(toFieldName, fromFieldName, to, from.Elem(), deepCopy, converters)
} else {
return false
}
Expand All @@ -529,14 +530,21 @@ func set(to, from reflect.Value, deepCopy bool, converters map[converterPair]Typ
}

// lookupAndCopyWithConverter looks up the type pair, on success the TypeConverter Fn func is called to copy src to dst field.
func lookupAndCopyWithConverter(to, from reflect.Value, converters map[converterPair]TypeConverter) (copied bool, err error) {
func lookupAndCopyWithConverter(toFieldName, fromFieldName string, to, from reflect.Value, converters map[converterPair]TypeConverter) (copied bool, err error) {
pair := converterPair{
SrcType: from.Type(),
DstType: to.Type(),
}

if cnv, ok := converters[pair]; ok {
result, err := cnv.Fn(from.Interface())
var result interface{}
var err error
if cnv.Fn != nil {
result, err = cnv.Fn(from.Interface())
}
if cnv.FnWithName != nil {
result, err = cnv.FnWithName(toFieldName, fromFieldName, from.Interface())
}

if err != nil {
return false, err
Expand Down
55 changes: 55 additions & 0 deletions copier_converter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,3 +185,58 @@ func TestCopyWithConverterStrToStrPointer(t *testing.T) {
t.Fatalf("got %q, wanted nil", *dst.Field1)
}
}

func TestCopyWithConverterAndFNWithName(t *testing.T) {
type SrcStruct struct {
Field1 string
Field2 string
}

type DestStruct struct {
Field1 string
Field2 string
}

src := SrcStruct{
Field1: "test",
Field2: "test2",
}

var dst DestStruct

err := copier.CopyWithOption(&dst, &src, copier.Option{
IgnoreEmpty: true,
DeepCopy: true,
Converters: []copier.TypeConverter{
{
SrcType: copier.String,
DstType: copier.String,
FnWithName: func(fromFieldName, toFieldName string, src interface{}) (interface{}, error) {
s, ok := src.(string)

if fromFieldName != "Field1" && fromFieldName != "Field2" {
t.Fatalf("fieldName is not in from object : f1:%s", fromFieldName)
}

if !ok {
return nil, errors.New("src type not matching")
}

return s + "2", nil
},
},
},
})

if err != nil {
t.Fatalf(`Should be able to copy from src to dst object. %v`, err)
return
}

if dst.Field1 != "test2" {
t.Fatalf("got %q, wanted %q", dst.Field1, "test2")
}
if dst.Field2 != "test22" {
t.Fatalf("got %q, wanted %q", dst.Field2, "test22")
}
}