Skip to content

Commit

Permalink
allow item in source array equal to nil
Browse files Browse the repository at this point in the history
  • Loading branch information
WingGao authored and jinzhu committed Jun 25, 2019
1 parent 2e0386f commit 9ab993b
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 38 deletions.
79 changes: 41 additions & 38 deletions copier.go
Expand Up @@ -55,65 +55,68 @@ func Copy(toValue interface{}, fromValue interface{}) (err error) {
} else {
source = indirect(from)
}

// dest
dest = indirect(reflect.New(toType).Elem())
} else {
source = indirect(from)
dest = indirect(to)
}

// Copy from field to field or method
for _, field := range deepFields(fromType) {
name := field.Name

if fromField := source.FieldByName(name); fromField.IsValid() {
// has field
if toField := dest.FieldByName(name); toField.IsValid() {
if toField.CanSet() {
if !set(toField, fromField) {
if err := Copy(toField.Addr().Interface(), fromField.Interface()); err != nil {
return err
// check source
if source.IsValid() {
fromTypeFields := deepFields(fromType)
//fmt.Printf("%#v", fromTypeFields)
// Copy from field to field or method
for _, field := range fromTypeFields {
name := field.Name

if fromField := source.FieldByName(name); fromField.IsValid() {
// has field
if toField := dest.FieldByName(name); toField.IsValid() {
if toField.CanSet() {
if !set(toField, fromField) {
if err := Copy(toField.Addr().Interface(), fromField.Interface()); err != nil {
return err
}
}
}
}
} else {
// try to set to method
var toMethod reflect.Value
if dest.CanAddr() {
toMethod = dest.Addr().MethodByName(name)
} else {
toMethod = dest.MethodByName(name)
}
// try to set to method
var toMethod reflect.Value
if dest.CanAddr() {
toMethod = dest.Addr().MethodByName(name)
} else {
toMethod = dest.MethodByName(name)
}

if toMethod.IsValid() && toMethod.Type().NumIn() == 1 && fromField.Type().AssignableTo(toMethod.Type().In(0)) {
toMethod.Call([]reflect.Value{fromField})
if toMethod.IsValid() && toMethod.Type().NumIn() == 1 && fromField.Type().AssignableTo(toMethod.Type().In(0)) {
toMethod.Call([]reflect.Value{fromField})
}
}
}
}
}

// Copy from method to field
for _, field := range deepFields(toType) {
name := field.Name
// Copy from method to field
for _, field := range deepFields(toType) {
name := field.Name

var fromMethod reflect.Value
if source.CanAddr() {
fromMethod = source.Addr().MethodByName(name)
} else {
fromMethod = source.MethodByName(name)
}
var fromMethod reflect.Value
if source.CanAddr() {
fromMethod = source.Addr().MethodByName(name)
} else {
fromMethod = source.MethodByName(name)
}

if fromMethod.IsValid() && fromMethod.Type().NumIn() == 0 && fromMethod.Type().NumOut() == 1 {
if toField := dest.FieldByName(name); toField.IsValid() && toField.CanSet() {
values := fromMethod.Call([]reflect.Value{})
if len(values) >= 1 {
set(toField, values[0])
if fromMethod.IsValid() && fromMethod.Type().NumIn() == 0 && fromMethod.Type().NumOut() == 1 {
if toField := dest.FieldByName(name); toField.IsValid() && toField.CanSet() {
values := fromMethod.Call([]reflect.Value{})
if len(values) >= 1 {
set(toField, values[0])
}
}
}
}
}

if isSlice {
if dest.Addr().Type().AssignableTo(to.Type().Elem()) {
to.Set(reflect.Append(to, dest.Addr()))
Expand Down
48 changes: 48 additions & 0 deletions copier_test.go
Expand Up @@ -90,6 +90,18 @@ func TestCopySameStructWithPointerField(t *testing.T) {
}
}

func checkEmployee2(employee Employee, user *User, t *testing.T, testCase string) {
if user == nil {
if employee.Name != "" || employee.Nickname != nil || employee.Birthday != nil || employee.Age != 0 ||
employee.DoubleAge != 0 || employee.FakeAge != 0 || employee.SuperRule != "" || employee.Notes != nil {
t.Errorf("%v : employee should be empty", testCase)
}
return
}

checkEmployee(employee, *user, t, testCase)
}

func TestCopyStruct(t *testing.T) {
var fakeAge int32 = 12
user := User{Name: "Jinzhu", Nickname: "jinzhu", Age: 18, FakeAge: &fakeAge, Role: "Admin", Notes: []string{"hello world", "welcome"}, flags: []byte{'x'}}
Expand Down Expand Up @@ -188,6 +200,42 @@ func TestCopyFromSliceToSlice(t *testing.T) {
}
}

func TestCopyFromSliceToSlice2(t *testing.T) {
users := []*User{{Name: "Jinzhu", Age: 18, Role: "Admin", Notes: []string{"hello world"}}, nil}
employees := []Employee{}

if copier.Copy(&employees, users); len(employees) != 2 {
t.Errorf("Should have two elems when copy slice to slice")
} else {
checkEmployee2(employees[0], users[0], t, "Copy From Slice To Slice Ptr @ 1")
checkEmployee2(employees[1], users[1], t, "Copy From Slice To Slice Ptr @ 2")
}

employees2 := &[]Employee{}
if copier.Copy(&employees2, &users); len(*employees2) != 2 {
t.Errorf("Should have two elems when copy slice to slice")
} else {
checkEmployee2((*employees2)[0], users[0], t, "Copy From Slice Ptr To Double Slice Ptr @ 1")
checkEmployee2((*employees2)[1], users[1], t, "Copy From Slice Ptr To Double Slice Ptr @ 2")
}

employees3 := []*Employee{}
if copier.Copy(&employees3, users); len(employees3) != 2 {
t.Errorf("Should have two elems when copy slice to slice")
} else {
checkEmployee2(*(employees3[0]), users[0], t, "Copy From Slice To Ptr Slice Ptr @ 1")
checkEmployee2(*(employees3[1]), users[1], t, "Copy From Slice To Ptr Slice Ptr @ 2")
}

employees4 := &[]*Employee{}
if copier.Copy(&employees4, users); len(*employees4) != 2 {
t.Errorf("Should have two elems when copy slice to slice")
} else {
checkEmployee2(*((*employees4)[0]), users[0], t, "Copy From Slice Ptr To Double Ptr Slice Ptr @ 1")
checkEmployee2(*((*employees4)[1]), users[1], t, "Copy From Slice Ptr To Double Ptr Slice Ptr @ 2")
}
}

func TestEmbedded(t *testing.T) {
type Base struct {
BaseField1 int
Expand Down

0 comments on commit 9ab993b

Please sign in to comment.