diff --git a/convertor/convertor.go b/convertor/convertor.go index a6a229b..e40b93b 100644 --- a/convertor/convertor.go +++ b/convertor/convertor.go @@ -9,6 +9,7 @@ import ( "encoding/binary" "encoding/gob" "encoding/json" + "errors" "fmt" "math" "reflect" @@ -340,3 +341,46 @@ func DeepClone[T any](src T) T { return result.Interface().(T) } + +// CopyProperties copies each field from the source into the destination. It recursively copies struct pointers and interfaces that contain struct pointers. +// Play: todo +func CopyProperties(dst, src interface{}) (err error) { + defer func() { + if e := recover(); e != nil { + err = errors.New(fmt.Sprintf("%v", e)) + } + }() + + dstType, dstValue := reflect.TypeOf(dst), reflect.ValueOf(dst) + srcType, srcValue := reflect.TypeOf(src), reflect.ValueOf(src) + + if dstType.Kind() != reflect.Ptr || dstType.Elem().Kind() != reflect.Struct { + return errors.New("CopyProperties: param dst should be struct pointer") + } + + if srcType.Kind() == reflect.Ptr { + srcType, srcValue = srcType.Elem(), srcValue.Elem() + } + if srcType.Kind() != reflect.Struct { + return errors.New("CopyProperties: param src should be a struct or struct pointer") + } + + dstType, dstValue = dstType.Elem(), dstValue.Elem() + + propertyNums := dstType.NumField() + + for i := 0; i < propertyNums; i++ { + property := dstType.Field(i) + propertyValue := srcValue.FieldByName(property.Name) + + if !propertyValue.IsValid() || property.Type != propertyValue.Type() { + continue + } + + if dstValue.Field(i).CanSet() { + dstValue.Field(i).Set(propertyValue) + } + } + + return nil +} diff --git a/convertor/convertor_test.go b/convertor/convertor_test.go index 68d4448..54bb019 100644 --- a/convertor/convertor_test.go +++ b/convertor/convertor_test.go @@ -298,3 +298,53 @@ func TestDeepClone(t *testing.T) { } } } + +func TestCopyProperties(t *testing.T) { + assert := internal.NewAssert(t, "TestCopyProperties") + + type Address struct { + Country string + ZipCode string + } + + type User struct { + Name string + Age int + Role string + Addr Address + Hobbys []string + salary int + } + + type Employee struct { + Name string + Age int + Role string + Addr Address + Hobbys []string + salary int + } + + user := User{Name: "user001", Age: 10, Role: "Admin", Addr: Address{Country: "CN", ZipCode: "001"}, Hobbys: []string{"a", "b"}, salary: 1000} + + employee1 := Employee{} + + err := CopyProperties(&employee1, &user) + + assert.IsNil(err) + assert.Equal("user001", employee1.Name) + assert.Equal("Admin", employee1.Role) + assert.Equal("CN", employee1.Addr.Country) + assert.Equal(0, employee1.salary) + + employee2 := Employee{Name: "employee001", Age: 20, Role: "User", + Addr: Address{Country: "UK", ZipCode: "002"}, salary: 500} + + err = CopyProperties(&employee2, &user) + + assert.IsNil(err) + assert.Equal("user001", employee2.Name) + assert.Equal("Admin", employee2.Role) + assert.Equal("CN", employee2.Addr.Country) + assert.Equal(500, employee2.salary) +}