pax_global_header00006660000000000000000000000064140551016400014506gustar00rootroot0000000000000052 comment=c72a821a0be1f70e87a87a98ea15f3771ac0461e copier-0.3.2/000077500000000000000000000000001405510164000127715ustar00rootroot00000000000000copier-0.3.2/.github/000077500000000000000000000000001405510164000143315ustar00rootroot00000000000000copier-0.3.2/.github/workflows/000077500000000000000000000000001405510164000163665ustar00rootroot00000000000000copier-0.3.2/.github/workflows/tests.yml000066400000000000000000000010551405510164000202540ustar00rootroot00000000000000name: tests on: push: branches-ignore: - 'gh-pages' pull_request: branches-ignore: - 'gh-pages' jobs: ci: strategy: matrix: go: ['1.15', '1.14', '1.13'] platform: [ubuntu-latest, macos-latest] # can not run in windows OS runs-on: ${{ matrix.platform }} steps: - name: Set up Go 1.x uses: actions/setup-go@v2 with: go-version: ${{ matrix.go }} - name: Check out code into the Go module directory uses: actions/checkout@v2 - name: Tests run: go test copier-0.3.2/License000066400000000000000000000020611405510164000142750ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2015 Jinzhu Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. copier-0.3.2/README.md000066400000000000000000000065471405510164000142640ustar00rootroot00000000000000# Copier I am a copier, I copy everything from one to another [![test status](https://github.com/jinzhu/copier/workflows/tests/badge.svg?branch=master "test status")](https://github.com/jinzhu/copier/actions) ## Features * Copy from field to field with same name * Copy from method to field with same name * Copy from field to method with same name * Copy from slice to slice * Copy from struct to slice * Copy from map to map * Enforce copying a field with a tag * Ignore a field with a tag * Deep Copy ## Usage ```go package main import ( "fmt" "github.com/jinzhu/copier" ) type User struct { Name string Role string Age int32 EmployeCode int64 `copier:"EmployeNum"` // specify field name // Explicitly ignored in the destination struct. Salary int } func (user *User) DoubleAge() int32 { return 2 * user.Age } // Tags in the destination Struct provide instructions to copier.Copy to ignore // or enforce copying and to panic or return an error if a field was not copied. type Employee struct { // Tell copier.Copy to panic if this field is not copied. Name string `copier:"must"` // Tell copier.Copy to return an error if this field is not copied. Age int32 `copier:"must,nopanic"` // Tell copier.Copy to explicitly ignore copying this field. Salary int `copier:"-"` DoubleAge int32 EmployeId int64 `copier:"EmployeNum"` // specify field name SuperRole string } func (employee *Employee) Role(role string) { employee.SuperRole = "Super " + role } func main() { var ( user = User{Name: "Jinzhu", Age: 18, Role: "Admin", Salary: 200000} users = []User{{Name: "Jinzhu", Age: 18, Role: "Admin", Salary: 100000}, {Name: "jinzhu 2", Age: 30, Role: "Dev", Salary: 60000}} employee = Employee{Salary: 150000} employees = []Employee{} ) copier.Copy(&employee, &user) fmt.Printf("%#v \n", employee) // Employee{ // Name: "Jinzhu", // Copy from field // Age: 18, // Copy from field // Salary:150000, // Copying explicitly ignored // DoubleAge: 36, // Copy from method // EmployeeId: 0, // Ignored // SuperRole: "Super Admin", // Copy to method // } // Copy struct to slice copier.Copy(&employees, &user) fmt.Printf("%#v \n", employees) // []Employee{ // {Name: "Jinzhu", Age: 18, Salary:0, DoubleAge: 36, EmployeId: 0, SuperRole: "Super Admin"} // } // Copy slice to slice employees = []Employee{} copier.Copy(&employees, &users) fmt.Printf("%#v \n", employees) // []Employee{ // {Name: "Jinzhu", Age: 18, Salary:0, DoubleAge: 36, EmployeId: 0, SuperRole: "Super Admin"}, // {Name: "jinzhu 2", Age: 30, Salary:0, DoubleAge: 60, EmployeId: 0, SuperRole: "Super Dev"}, // } // Copy map to map map1 := map[int]int{3: 6, 4: 8} map2 := map[int32]int8{} copier.Copy(&map2, map1) fmt.Printf("%#v \n", map2) // map[int32]int8{3:6, 4:8} } ``` ### Copy with Option ```go copier.CopyWithOption(&to, &from, copier.Option{IgnoreEmpty: true, DeepCopy: true}) ``` ## Contributing You can help to make the project better, check out [http://gorm.io/contribute.html](http://gorm.io/contribute.html) for things you can do. # Author **jinzhu** * * * ## License Released under the [MIT License](https://github.com/jinzhu/copier/blob/master/License). copier-0.3.2/copier.go000066400000000000000000000360371405510164000146120ustar00rootroot00000000000000package copier import ( "database/sql" "database/sql/driver" "errors" "fmt" "reflect" "strings" "unicode" ) // These flags define options for tag handling const ( // Denotes that a destination field must be copied to. If copying fails then a panic will ensue. tagMust uint8 = 1 << iota // Denotes that the program should not panic when the must flag is on and // value is not copied. The program will return an error instead. tagNoPanic // Ignore a destination field from being copied to. tagIgnore // Denotes that the value as been copied hasCopied ) // Option sets copy options 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 } // Tag Flags type flags struct { BitFlags map[string]uint8 SrcNames tagNameMapping DestNames tagNameMapping } // Field Tag name mapping type tagNameMapping struct { FieldNameToTag map[string]string TagToFieldName map[string]string } // Copy copy things func Copy(toValue interface{}, fromValue interface{}) (err error) { return copier(toValue, fromValue, Option{}) } // CopyWithOption copy with option func CopyWithOption(toValue interface{}, fromValue interface{}, opt Option) (err error) { return copier(toValue, fromValue, opt) } func copier(toValue interface{}, fromValue interface{}, opt Option) (err error) { var ( isSlice bool amount = 1 from = indirect(reflect.ValueOf(fromValue)) to = indirect(reflect.ValueOf(toValue)) ) if !to.CanAddr() { return ErrInvalidCopyDestination } // Return is from value is invalid if !from.IsValid() { return ErrInvalidCopyFrom } fromType, isPtrFrom := indirectType(from.Type()) toType, _ := indirectType(to.Type()) if fromType.Kind() == reflect.Interface { fromType = reflect.TypeOf(from.Interface()) } if toType.Kind() == reflect.Interface { toType, _ = indirectType(reflect.TypeOf(to.Interface())) oldTo := to to = reflect.New(reflect.TypeOf(to.Interface())).Elem() defer func() { oldTo.Set(to) }() } // Just set it if possible to assign for normal types if from.Kind() != reflect.Slice && from.Kind() != reflect.Struct && from.Kind() != reflect.Map && (from.Type().AssignableTo(to.Type()) || from.Type().ConvertibleTo(to.Type())) { if !isPtrFrom || !opt.DeepCopy { to.Set(from.Convert(to.Type())) } else { fromCopy := reflect.New(from.Type()) fromCopy.Set(from.Elem()) to.Set(fromCopy.Convert(to.Type())) } return } if from.Kind() != reflect.Slice && fromType.Kind() == reflect.Map && toType.Kind() == reflect.Map { if !fromType.Key().ConvertibleTo(toType.Key()) { return ErrMapKeyNotMatch } if to.IsNil() { to.Set(reflect.MakeMapWithSize(toType, from.Len())) } for _, k := range from.MapKeys() { toKey := indirect(reflect.New(toType.Key())) if !set(toKey, k, opt.DeepCopy) { return fmt.Errorf("%w map, old key: %v, new key: %v", ErrNotSupported, k.Type(), toType.Key()) } elemType, _ := indirectType(toType.Elem()) toValue := indirect(reflect.New(elemType)) if !set(toValue, from.MapIndex(k), opt.DeepCopy) { if err = copier(toValue.Addr().Interface(), from.MapIndex(k).Interface(), opt); err != nil { return err } } for { if elemType == toType.Elem() { to.SetMapIndex(toKey, toValue) break } elemType = reflect.PtrTo(elemType) toValue = toValue.Addr() } } return } if from.Kind() == reflect.Slice && to.Kind() == reflect.Slice && fromType.ConvertibleTo(toType) { 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) { // ignore error while copy slice element err = copier(to.Index(i).Addr().Interface(), from.Index(i).Interface(), opt) if err != nil { continue } } } return } if fromType.Kind() != reflect.Struct || toType.Kind() != reflect.Struct { // skip not supported type return } if from.Kind() == reflect.Slice || to.Kind() == reflect.Slice { isSlice = true if from.Kind() == reflect.Slice { amount = from.Len() } } for i := 0; i < amount; i++ { var dest, source reflect.Value if isSlice { // source if from.Kind() == reflect.Slice { source = indirect(from.Index(i)) } else { source = indirect(from) } // dest dest = indirect(reflect.New(toType).Elem()) } else { source = indirect(from) dest = indirect(to) } destKind := dest.Kind() initDest := false if destKind == reflect.Interface { initDest = true dest = indirect(reflect.New(toType)) } // Get tag options flgs, err := getFlags(dest, source, toType, fromType) if err != nil { return err } // check source if source.IsValid() { // Copy from source field to dest field or method fromTypeFields := deepFields(fromType) for _, field := range fromTypeFields { name := field.Name // Get bit flags for field 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) { // process for nested anonymous field destFieldNotSet := false if f, ok := dest.Type().FieldByName(destFieldName); ok { for idx := range f.Index { destField := dest.FieldByIndex(f.Index[:idx+1]) if destField.Kind() != reflect.Ptr { continue } if !destField.IsNil() { continue } if !destField.CanSet() { destFieldNotSet = true break } // destField is a nil pointer that can be set newValue := reflect.New(destField.Type().Elem()) destField.Set(newValue) } } if destFieldNotSet { break } toField := dest.FieldByName(destFieldName) if toField.IsValid() { if toField.CanSet() { if !set(toField, fromField, opt.DeepCopy) { if err := copier(toField.Addr().Interface(), fromField.Interface(), opt); err != nil { return err } } if fieldFlags != 0 { // Note that a copy was made flgs.BitFlags[name] = fieldFlags | hasCopied } } } else { // try to set to method var toMethod reflect.Value if dest.CanAddr() { toMethod = dest.Addr().MethodByName(destFieldName) } else { toMethod = dest.MethodByName(destFieldName) } if toMethod.IsValid() && toMethod.Type().NumIn() == 1 && fromField.Type().AssignableTo(toMethod.Type().In(0)) { toMethod.Call([]reflect.Value{fromField}) } } } } // Copy from from method to dest field for _, field := range deepFields(toType) { name := field.Name srcFieldName, destFieldName := getFieldName(name, flgs) var fromMethod reflect.Value if source.CanAddr() { fromMethod = source.Addr().MethodByName(srcFieldName) } else { fromMethod = source.MethodByName(srcFieldName) } if fromMethod.IsValid() && fromMethod.Type().NumIn() == 0 && fromMethod.Type().NumOut() == 1 && !shouldIgnore(fromMethod, opt.IgnoreEmpty) { if toField := dest.FieldByName(destFieldName); toField.IsValid() && toField.CanSet() { values := fromMethod.Call([]reflect.Value{}) if len(values) >= 1 { set(toField, values[0], opt.DeepCopy) } } } } } if isSlice && to.Kind() == reflect.Slice { if dest.Addr().Type().AssignableTo(to.Type().Elem()) { if to.Len() < i+1 { to.Set(reflect.Append(to, dest.Addr())) } else { if !set(to.Index(i), dest.Addr(), opt.DeepCopy) { // ignore error while copy slice element err = copier(to.Index(i).Addr().Interface(), dest.Addr().Interface(), opt) if err != nil { continue } } } } else if dest.Type().AssignableTo(to.Type().Elem()) { if to.Len() < i+1 { to.Set(reflect.Append(to, dest)) } else { if !set(to.Index(i), dest, opt.DeepCopy) { // ignore error while copy slice element err = copier(to.Index(i).Addr().Interface(), dest.Interface(), opt) if err != nil { continue } } } } } else if initDest { to.Set(dest) } err = checkBitFlags(flgs.BitFlags) } return } func shouldIgnore(v reflect.Value, ignoreEmpty bool) bool { if !ignoreEmpty { return false } return v.IsZero() } func deepFields(reflectType reflect.Type) []reflect.StructField { if reflectType, _ = indirectType(reflectType); reflectType.Kind() == reflect.Struct { fields := make([]reflect.StructField, 0, reflectType.NumField()) for i := 0; i < reflectType.NumField(); i++ { v := reflectType.Field(i) if v.Anonymous { fields = append(fields, deepFields(v.Type)...) } else { fields = append(fields, v) } } return fields } return nil } func indirect(reflectValue reflect.Value) reflect.Value { for reflectValue.Kind() == reflect.Ptr { reflectValue = reflectValue.Elem() } return reflectValue } func indirectType(reflectType reflect.Type) (_ reflect.Type, isPtr bool) { for reflectType.Kind() == reflect.Ptr || reflectType.Kind() == reflect.Slice { reflectType = reflectType.Elem() isPtr = true } return reflectType, isPtr } func set(to, from reflect.Value, deepCopy bool) bool { if from.IsValid() { 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 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 { // `from` -> `to` // sql.NullString -> string v, err := fromValuer.Value() if err != nil { return false } // if `from` is not valid do nothing with `to` if v == nil { return true } 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) } else { return false } } return true } // parseTags Parses struct tags and returns uint8 bit flags. func parseTags(tag string) (flg uint8, name string, err error) { for _, t := range strings.Split(tag, ",") { switch t { case "-": flg = tagIgnore return case "must": flg = flg | tagMust case "nopanic": flg = flg | tagNoPanic default: if unicode.IsUpper([]rune(t)[0]) { name = strings.TrimSpace(t) } else { err = errors.New("copier field name tag must be start upper case") } } } return } // getTagFlags Parses struct tags for bit flags, field name. func getFlags(dest, src reflect.Value, toType, fromType reflect.Type) (flags, error) { flgs := flags{ BitFlags: map[string]uint8{}, SrcNames: tagNameMapping{ FieldNameToTag: map[string]string{}, TagToFieldName: map[string]string{}, }, DestNames: tagNameMapping{ FieldNameToTag: map[string]string{}, TagToFieldName: map[string]string{}, }, } var toTypeFields, fromTypeFields []reflect.StructField if dest.IsValid() { toTypeFields = deepFields(toType) } if src.IsValid() { fromTypeFields = deepFields(fromType) } // Get a list dest of tags for _, field := range toTypeFields { tags := field.Tag.Get("copier") if tags != "" { var name string var err error if flgs.BitFlags[field.Name], name, err = parseTags(tags); err != nil { return flags{}, err } else if name != "" { flgs.DestNames.FieldNameToTag[field.Name] = name flgs.DestNames.TagToFieldName[name] = field.Name } } } // Get a list source of tags for _, field := range fromTypeFields { tags := field.Tag.Get("copier") if tags != "" { var name string var err error if _, name, err = parseTags(tags); err != nil { return flags{}, err } else if name != "" { flgs.SrcNames.FieldNameToTag[field.Name] = name flgs.SrcNames.TagToFieldName[name] = field.Name } } } return flgs, nil } // checkBitFlags Checks flags for error or panic conditions. func checkBitFlags(flagsList map[string]uint8) (err error) { // Check flag conditions were met for name, flgs := range flagsList { if flgs&hasCopied == 0 { switch { case flgs&tagMust != 0 && flgs&tagNoPanic != 0: err = fmt.Errorf("field %s has must tag but was not copied", name) return case flgs&(tagMust) != 0: panic(fmt.Sprintf("Field %s has must tag but was not copied", name)) } } } return } func getFieldName(fieldName string, flgs flags) (srcFieldName string, destFieldName string) { // get dest field name if srcTagName, ok := flgs.SrcNames.FieldNameToTag[fieldName]; ok { destFieldName = srcTagName if destTagName, ok := flgs.DestNames.TagToFieldName[srcTagName]; ok { destFieldName = destTagName } } else { if destTagName, ok := flgs.DestNames.TagToFieldName[fieldName]; ok { destFieldName = destTagName } } if destFieldName == "" { destFieldName = fieldName } // get source field name if destTagName, ok := flgs.DestNames.FieldNameToTag[fieldName]; ok { srcFieldName = destTagName if srcField, ok := flgs.SrcNames.TagToFieldName[destTagName]; ok { srcFieldName = srcField } } else { if srcField, ok := flgs.SrcNames.TagToFieldName[fieldName]; ok { srcFieldName = srcField } } if srcFieldName == "" { srcFieldName = fieldName } return } func driverValuer(v reflect.Value) (i driver.Valuer, ok bool) { if !v.CanAddr() { i, ok = v.Interface().(driver.Valuer) return } i, ok = v.Addr().Interface().(driver.Valuer) return } copier-0.3.2/copier_benchmark_test.go000066400000000000000000000031531405510164000176540ustar00rootroot00000000000000package copier_test import ( "encoding/json" "testing" "github.com/jinzhu/copier" ) func BenchmarkCopyStruct(b *testing.B) { var fakeAge int32 = 12 user := User{Name: "Jinzhu", Nickname: "jinzhu", Age: 18, FakeAge: &fakeAge, Role: "Admin", Notes: []string{"hello world", "welcome"}, flags: []byte{'x'}} for x := 0; x < b.N; x++ { copier.Copy(&Employee{}, &user) } } func BenchmarkCopyStructFields(b *testing.B) { var fakeAge int32 = 12 user := User{Name: "Jinzhu", Nickname: "jinzhu", Age: 18, FakeAge: &fakeAge, Role: "Admin", Notes: []string{"hello world", "welcome"}, flags: []byte{'x'}} for x := 0; x < b.N; x++ { copier.Copy(&Employee{}, &user) } } func BenchmarkNamaCopy(b *testing.B) { var fakeAge int32 = 12 user := User{Name: "Jinzhu", Nickname: "jinzhu", Age: 18, FakeAge: &fakeAge, Role: "Admin", Notes: []string{"hello world", "welcome"}, flags: []byte{'x'}} for x := 0; x < b.N; x++ { employee := &Employee{ Name: user.Name, Nickname: &user.Nickname, Age: int64(user.Age), FakeAge: int(*user.FakeAge), DoubleAge: user.DoubleAge(), } for _, note := range user.Notes { employee.Notes = append(employee.Notes, ¬e) } employee.Role(user.Role) } } func BenchmarkJsonMarshalCopy(b *testing.B) { var fakeAge int32 = 12 user := User{Name: "Jinzhu", Nickname: "jinzhu", Age: 18, FakeAge: &fakeAge, Role: "Admin", Notes: []string{"hello world", "welcome"}, flags: []byte{'x'}} for x := 0; x < b.N; x++ { data, _ := json.Marshal(user) var employee Employee json.Unmarshal(data, &employee) employee.DoubleAge = user.DoubleAge() employee.Role(user.Role) } } copier-0.3.2/copier_different_type_test.go000066400000000000000000000054451405510164000207370ustar00rootroot00000000000000package copier_test import ( "testing" "github.com/jinzhu/copier" ) type TypeStruct1 struct { Field1 string Field2 string Field3 TypeStruct2 Field4 *TypeStruct2 Field5 []*TypeStruct2 Field6 []TypeStruct2 Field7 []*TypeStruct2 Field8 []TypeStruct2 Field9 []string } type TypeStruct2 struct { Field1 int Field2 string Field3 []TypeStruct2 Field4 *TypeStruct2 Field5 *TypeStruct2 Field9 string } type TypeStruct3 struct { Field1 interface{} Field2 string Field3 TypeStruct4 Field4 *TypeStruct4 Field5 []*TypeStruct4 Field6 []*TypeStruct4 Field7 []TypeStruct4 Field8 []TypeStruct4 } type TypeStruct4 struct { field1 int Field2 string } func (t *TypeStruct4) Field1(i int) { t.field1 = i } func TestCopyDifferentFieldType(t *testing.T) { ts := &TypeStruct1{ Field1: "str1", Field2: "str2", } ts2 := &TypeStruct2{} copier.Copy(ts2, ts) if ts2.Field2 != ts.Field2 || ts2.Field1 != 0 { t.Errorf("Should be able to copy from ts to ts2") } } func TestCopyDifferentTypeMethod(t *testing.T) { ts := &TypeStruct1{ Field1: "str1", Field2: "str2", } ts4 := &TypeStruct4{} copier.Copy(ts4, ts) if ts4.Field2 != ts.Field2 || ts4.field1 != 0 { t.Errorf("Should be able to copy from ts to ts4") } } func TestAssignableType(t *testing.T) { ts := &TypeStruct1{ Field1: "str1", Field2: "str2", Field3: TypeStruct2{ Field1: 666, Field2: "str2", }, Field4: &TypeStruct2{ Field1: 666, Field2: "str2", }, Field5: []*TypeStruct2{ { Field1: 666, Field2: "str2", }, }, Field6: []TypeStruct2{ { Field1: 666, Field2: "str2", }, }, Field7: []*TypeStruct2{ { Field1: 666, Field2: "str2", }, }, } ts3 := &TypeStruct3{} copier.Copy(&ts3, &ts) if v, ok := ts3.Field1.(string); !ok { t.Error("Assign to interface{} type did not succeed") } else if v != "str1" { t.Error("String haven't been copied correctly") } if ts3.Field2 != ts.Field2 { t.Errorf("Field2 should be copied") } checkType2WithType4(ts.Field3, ts3.Field3, t, "Field3") checkType2WithType4(*ts.Field4, *ts3.Field4, t, "Field4") if len(ts3.Field5) != len(ts.Field5) { t.Fatalf("fields not equal, got %v, expects: %v", len(ts3.Field5), len(ts.Field5)) } for idx, f := range ts.Field5 { checkType2WithType4(*f, *(ts3.Field5[idx]), t, "Field5") } for idx, f := range ts.Field6 { checkType2WithType4(f, *(ts3.Field6[idx]), t, "Field6") } for idx, f := range ts.Field7 { checkType2WithType4(*f, ts3.Field7[idx], t, "Field7") } for idx, f := range ts.Field8 { checkType2WithType4(f, ts3.Field8[idx], t, "Field8") } } func checkType2WithType4(t2 TypeStruct2, t4 TypeStruct4, t *testing.T, testCase string) { if t2.Field1 != t4.field1 || t2.Field2 != t4.Field2 { t.Errorf("%v: type struct 4 and type struct 2 is not equal", testCase) } } copier-0.3.2/copier_issue84_test.go000066400000000000000000000033331405510164000172260ustar00rootroot00000000000000package copier_test import ( "testing" "time" "github.com/jinzhu/copier" ) type Embedded struct { Field1 string Field2 string } type Embedder struct { Embedded PtrField *string } type Timestamps struct { CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } type NotWork struct { ID string `json:"id"` UserID *string `json:"user_id"` Name string `json:"name"` Website *string `json:"website"` Timestamps } type Work struct { ID string `json:"id"` Name string `json:"name"` UserID *string `json:"user_id"` Website *string `json:"website"` Timestamps } func TestIssue84(t *testing.T) { t.Run("test1", func(t *testing.T) { var embedder Embedder embedded := Embedded{ Field1: "1", Field2: "2", } err := copier.Copy(&embedder, &embedded) if err != nil { t.Errorf("unable to copy: %s", err) } if embedder.Field1 != embedded.Field1 { t.Errorf("field1 value is %s instead of %s", embedder.Field1, embedded.Field1) } if embedder.Field2 != embedded.Field2 { t.Errorf("field2 value is %s instead of %s", embedder.Field2, embedded.Field2) } }) t.Run("from issue", func(t *testing.T) { notWorkObj := NotWork{ ID: "123", Name: "name", Website: nil, UserID: nil, Timestamps: Timestamps{ UpdatedAt: time.Now(), }, } workObj := Work{ ID: "123", Name: "name", Website: nil, UserID: nil, Timestamps: Timestamps{ UpdatedAt: time.Now(), }, } destObj1 := Work{} destObj2 := NotWork{} copier.CopyWithOption(&destObj1, &workObj, copier.Option{IgnoreEmpty: true, DeepCopy: false}) copier.CopyWithOption(&destObj2, ¬WorkObj, copier.Option{IgnoreEmpty: true, DeepCopy: false}) }) } copier-0.3.2/copier_tags_test.go000066400000000000000000000037151405510164000166640ustar00rootroot00000000000000package copier_test import ( "testing" "github.com/jinzhu/copier" ) type EmployeeTags struct { Name string `copier:"must"` DOB string Address string ID int `copier:"-"` } type User1 struct { Name string DOB string Address string ID int } type User2 struct { DOB string Address string ID int } func TestCopyTagIgnore(t *testing.T) { employee := EmployeeTags{ID: 100} user := User1{Name: "Dexter Ledesma", DOB: "1 November, 1970", Address: "21 Jump Street", ID: 12345} copier.Copy(&employee, user) if employee.ID == user.ID { t.Error("Was not expected to copy IDs") } if employee.ID != 100 { t.Error("Original ID was overwritten") } } func TestCopyTagMust(t *testing.T) { employee := &EmployeeTags{} user := &User2{DOB: "1 January 1970"} defer func() { if r := recover(); r == nil { t.Error("Expected a panic.") } }() copier.Copy(employee, user) } func TestCopyTagFieldName(t *testing.T) { t.Run("another name field copy", func(t *testing.T) { type SrcTags struct { FieldA string FieldB string `copier:"Field2"` FieldC string `copier:"FieldTagMatch"` } type DestTags struct { Field1 string `copier:"FieldA"` Field2 string Field3 string `copier:"FieldTagMatch"` } dst := &DestTags{} src := &SrcTags{ FieldA: "FieldA->Field1", FieldB: "FieldB->Field2", FieldC: "FieldC->Field3", } err := copier.Copy(dst, src) if err != nil { t.Fatal(err) } if dst.Field1 != src.FieldA { t.Error("Field1 no copy") } if dst.Field2 != src.FieldB { t.Error("Field2 no copy") } if dst.Field3 != src.FieldC { t.Error("Field3 no copy") } }) t.Run("validate error flag name", func(t *testing.T) { type SrcTags struct { field string } type DestTags struct { Field1 string `copier:"field"` } dst := &DestTags{} src := &SrcTags{ field: "field->Field1", } err := copier.Copy(dst, src) if err == nil { t.Fatal("must validate error") } }) } copier-0.3.2/copier_test.go000066400000000000000000000751201405510164000156450ustar00rootroot00000000000000package copier_test import ( "database/sql" "errors" "fmt" "testing" "time" "github.com/jinzhu/copier" ) type User struct { Name string Birthday *time.Time Nickname string Role string Age int32 FakeAge *int32 Notes []string flags []byte } func (user User) DoubleAge() int32 { return 2 * user.Age } type Employee struct { Name string Birthday *time.Time Nickname *string Age int64 FakeAge int EmployeID int64 DoubleAge int32 SuperRule string Notes []*string flags []byte } func (employee *Employee) Role(role string) { employee.SuperRule = "Super " + role } func checkEmployee(employee Employee, user User, t *testing.T, testCase string) { if employee.Name != user.Name { t.Errorf("%v: Name haven't been copied correctly.", testCase) } if employee.Nickname == nil || *employee.Nickname != user.Nickname { t.Errorf("%v: NickName haven't been copied correctly.", testCase) } if employee.Birthday == nil && user.Birthday != nil { t.Errorf("%v: Birthday haven't been copied correctly.", testCase) } if employee.Birthday != nil && user.Birthday == nil { t.Errorf("%v: Birthday haven't been copied correctly.", testCase) } if employee.Birthday != nil && user.Birthday != nil && !employee.Birthday.Equal(*(user.Birthday)) { t.Errorf("%v: Birthday haven't been copied correctly.", testCase) } if employee.Age != int64(user.Age) { t.Errorf("%v: Age haven't been copied correctly.", testCase) } if user.FakeAge != nil && employee.FakeAge != int(*user.FakeAge) { t.Errorf("%v: FakeAge haven't been copied correctly.", testCase) } if employee.DoubleAge != user.DoubleAge() { t.Errorf("%v: Copy from method doesn't work", testCase) } if employee.SuperRule != "Super "+user.Role { t.Errorf("%v: Copy to method doesn't work", testCase) } if len(employee.Notes) != len(user.Notes) { t.Fatalf("%v: Copy from slice doesn't work, employee notes len: %v, user: %v", testCase, len(employee.Notes), len(user.Notes)) } for idx, note := range user.Notes { if note != *employee.Notes[idx] { t.Fatalf("%v: Copy from slice doesn't work, notes idx: %v employee: %v user: %v", testCase, idx, *employee.Notes[idx], note) } } } func TestCopySameStructWithPointerField(t *testing.T) { var fakeAge int32 = 12 var currentTime time.Time = time.Now() user := &User{Birthday: ¤tTime, Name: "Jinzhu", Nickname: "jinzhu", Age: 18, FakeAge: &fakeAge, Role: "Admin", Notes: []string{"hello world", "welcome"}, flags: []byte{'x'}} newUser := &User{} copier.Copy(newUser, user) if user.Birthday == newUser.Birthday { t.Errorf("TestCopySameStructWithPointerField: copy Birthday failed since they need to have different address") } if user.FakeAge == newUser.FakeAge { t.Errorf("TestCopySameStructWithPointerField: copy FakeAge failed since they need to have different address") } } 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'}} employee := Employee{} if err := copier.Copy(employee, &user); err == nil { t.Errorf("Copy to unaddressable value should get error") } copier.Copy(&employee, &user) checkEmployee(employee, user, t, "Copy From Ptr To Ptr") employee2 := Employee{} copier.Copy(&employee2, user) checkEmployee(employee2, user, t, "Copy From Struct To Ptr") employee3 := Employee{} ptrToUser := &user copier.Copy(&employee3, &ptrToUser) checkEmployee(employee3, user, t, "Copy From Double Ptr To Ptr") employee4 := &Employee{} copier.Copy(&employee4, user) checkEmployee(*employee4, user, t, "Copy From Ptr To Double Ptr") } func TestCopyFromStructToSlice(t *testing.T) { user := User{Name: "Jinzhu", Age: 18, Role: "Admin", Notes: []string{"hello world"}} employees := []Employee{} if err := copier.Copy(employees, &user); err != nil && len(employees) != 0 { t.Errorf("Copy to unaddressable value should get error") } if copier.Copy(&employees, &user); len(employees) != 1 { t.Errorf("Should only have one elem when copy struct to slice") } else { checkEmployee(employees[0], user, t, "Copy From Struct To Slice Ptr") } employees2 := &[]Employee{} if copier.Copy(&employees2, user); len(*employees2) != 1 { t.Errorf("Should only have one elem when copy struct to slice") } else { checkEmployee((*employees2)[0], user, t, "Copy From Struct To Double Slice Ptr") } employees3 := []*Employee{} if copier.Copy(&employees3, user); len(employees3) != 1 { t.Errorf("Should only have one elem when copy struct to slice") } else { checkEmployee(*(employees3[0]), user, t, "Copy From Struct To Ptr Slice Ptr") } employees4 := &[]*Employee{} if copier.Copy(&employees4, user); len(*employees4) != 1 { t.Errorf("Should only have one elem when copy struct to slice") } else { checkEmployee(*((*employees4)[0]), user, t, "Copy From Struct To Double Ptr Slice Ptr") } } func TestCopyFromSliceToSlice(t *testing.T) { users := []User{{Name: "Jinzhu", Age: 18, Role: "Admin", Notes: []string{"hello world"}}, {Name: "Jinzhu2", Age: 22, Role: "Dev", Notes: []string{"hello world", "hello"}}} employees := []Employee{} if copier.Copy(&employees, users); len(employees) != 2 { t.Errorf("Should have two elems when copy slice to slice") } else { checkEmployee(employees[0], users[0], t, "Copy From Slice To Slice Ptr @ 1") checkEmployee(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 { checkEmployee((*employees2)[0], users[0], t, "Copy From Slice Ptr To Double Slice Ptr @ 1") checkEmployee((*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 { checkEmployee(*(employees3[0]), users[0], t, "Copy From Slice To Ptr Slice Ptr @ 1") checkEmployee(*(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 { checkEmployee(*((*employees4)[0]), users[0], t, "Copy From Slice Ptr To Double Ptr Slice Ptr @ 1") checkEmployee(*((*employees4)[1]), users[1], t, "Copy From Slice Ptr To Double Ptr Slice Ptr @ 2") } } 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 TestCopyFromSliceToSlice3(t *testing.T) { type CollectionAlias struct { CollectionName string `json:"collection_name"` Name string `json:"name"` } expectedResult := []*CollectionAlias{ {"collection", "collection_alias1"}, {"collection", "collection_alias2"}, {"collection", "collection_alias3"}, } mockedResult := []*CollectionAlias{} copier.Copy(&mockedResult, &expectedResult) if len(mockedResult) != len(expectedResult) { t.Fatalf("failed to copy results") } for idx := range mockedResult { if mockedResult[idx].Name != mockedResult[idx].Name || mockedResult[idx].CollectionName != mockedResult[idx].CollectionName { t.Fatalf("failed to copy results") } } } func TestEmbeddedAndBase(t *testing.T) { type Base struct { BaseField1 int BaseField2 int User *User } type Embed struct { EmbedField1 int EmbedField2 int Base } base := Base{} embedded := Embed{} embedded.BaseField1 = 1 embedded.BaseField2 = 2 embedded.EmbedField1 = 3 embedded.EmbedField2 = 4 user := User{ Name: "testName", } embedded.User = &user copier.Copy(&base, &embedded) if base.BaseField1 != 1 || base.User.Name != "testName" { t.Error("Embedded fields not copied") } base.BaseField1 = 11 base.BaseField2 = 12 user1 := User{ Name: "testName1", } base.User = &user1 copier.Copy(&embedded, &base) if embedded.BaseField1 != 11 || embedded.User.Name != "testName1" { t.Error("base fields not copied") } } func TestStructField(t *testing.T) { type Detail struct { Info1 string Info2 *string } type SimilarDetail struct { Info1 string Info2 *string } type UserWithDetailsPtr struct { Details []*Detail Detail *Detail } type UserWithDetails struct { Details []Detail Detail Detail } type UserWithSimilarDetailsPtr struct { Detail *SimilarDetail } type UserWithSimilarDetails struct { Detail SimilarDetail } type EmployeeWithDetails struct { Detail Detail } type EmployeeWithDetailsPtr struct { Detail *Detail } type EmployeeWithSimilarDetails struct { Detail SimilarDetail } type EmployeeWithSimilarDetailsPtr struct { Detail *SimilarDetail } optionsDeepCopy := copier.Option{ DeepCopy: true, } checkDetail := func(t *testing.T, source Detail, target Detail) { if source.Info1 != target.Info1 { t.Errorf("info1 is diff: source: %v, target: %v", source.Info1, target.Info1) } if (source.Info2 != nil || target.Info2 != nil) && (*source.Info2 != *target.Info2) { t.Errorf("info2 is diff: source: %v, target: %v", *source.Info2, *target.Info2) } } t.Run("Should work without deepCopy", func(t *testing.T) { t.Run("Should work with same type and both ptr field", func(t *testing.T) { info2 := "world" from := UserWithDetailsPtr{ Detail: &Detail{Info1: "hello", Info2: &info2}, Details: []*Detail{{Info1: "hello", Info2: &info2}}, } to := UserWithDetailsPtr{} copier.Copy(&to, from) checkDetail(t, *from.Detail, *to.Detail) *to.Detail.Info2 = "new value" if *from.Detail.Info2 != *to.Detail.Info2 { t.Fatalf("DeepCopy not enabled") } if len(to.Details) != len(to.Details) { t.Fatalf("slice should be copied") } for idx, detail := range from.Details { checkDetail(t, *detail, *to.Details[idx]) } }) t.Run("Should work with same type and both not ptr field", func(t *testing.T) { info2 := "world" from := UserWithDetails{ Detail: Detail{Info1: "hello", Info2: &info2}, Details: []Detail{{Info1: "hello", Info2: &info2}}, } to := UserWithDetails{} copier.Copy(&to, from) checkDetail(t, from.Detail, to.Detail) *to.Detail.Info2 = "new value" if *from.Detail.Info2 != *to.Detail.Info2 { t.Fatalf("DeepCopy not enabled") } if len(to.Details) != len(to.Details) { t.Fatalf("slice should be copied") } for idx, detail := range from.Details { checkDetail(t, detail, to.Details[idx]) } }) t.Run("Should work with different type and both ptr field", func(t *testing.T) { info2 := "world" from := UserWithDetailsPtr{Detail: &Detail{Info1: "hello", Info2: &info2}} to := EmployeeWithDetailsPtr{} copier.Copy(&to, from) newValue := "new value" to.Detail.Info2 = &newValue if to.Detail.Info1 == "" { t.Errorf("should not be empty") } if to.Detail.Info1 != from.Detail.Info1 { t.Errorf("should be the same") } if to.Detail.Info2 == from.Detail.Info2 { t.Errorf("should be different") } }) t.Run("Should work with different type and both not ptr field", func(t *testing.T) { info2 := "world" from := UserWithDetails{Detail: Detail{Info1: "hello", Info2: &info2}} to := EmployeeWithDetails{} copier.Copy(&to, from) newValue := "new value" to.Detail.Info2 = &newValue if to.Detail.Info1 == "" { t.Errorf("should not be empty") } if to.Detail.Info1 != from.Detail.Info1 { t.Errorf("should be the same") } if to.Detail.Info2 == from.Detail.Info2 { t.Errorf("should be different") } }) t.Run("Should work with from ptr field and to not ptr field", func(t *testing.T) { info2 := "world" from := UserWithDetailsPtr{Detail: &Detail{Info1: "hello", Info2: &info2}} to := EmployeeWithDetails{} copier.Copy(&to, from) newValue := "new value" to.Detail.Info2 = &newValue if to.Detail.Info1 == "" { t.Errorf("should not be empty") } if to.Detail.Info1 != from.Detail.Info1 { t.Errorf("should be the same") } if to.Detail.Info2 == from.Detail.Info2 { t.Errorf("should be different") } }) t.Run("Should work with from not ptr field and to ptr field", func(t *testing.T) { info2 := "world" from := UserWithDetails{Detail: Detail{Info1: "hello", Info2: &info2}} to := EmployeeWithDetailsPtr{} copier.Copy(&to, from) newValue := "new value" to.Detail.Info2 = &newValue if to.Detail.Info1 == "" { t.Errorf("should not be empty") } if to.Detail.Info1 != from.Detail.Info1 { t.Errorf("should be the same") } if to.Detail.Info2 == from.Detail.Info2 { t.Errorf("should be different") } }) }) t.Run("Should work with deepCopy", func(t *testing.T) { t.Run("Should work with same type and both ptr field", func(t *testing.T) { info2 := "world" from := UserWithDetailsPtr{ Detail: &Detail{Info1: "hello", Info2: &info2}, Details: []*Detail{{Info1: "hello", Info2: &info2}}, } to := UserWithDetailsPtr{} copier.CopyWithOption(&to, from, optionsDeepCopy) checkDetail(t, *from.Detail, *to.Detail) *to.Detail.Info2 = "new value" if *from.Detail.Info2 == *to.Detail.Info2 { t.Fatalf("DeepCopy enabled") } if len(to.Details) != len(to.Details) { t.Fatalf("slice should be copied") } for idx, detail := range from.Details { checkDetail(t, *detail, *to.Details[idx]) } }) t.Run("Should work with same type and both not ptr field", func(t *testing.T) { info2 := "world" from := UserWithDetails{ Detail: Detail{Info1: "hello", Info2: &info2}, Details: []Detail{{Info1: "hello", Info2: &info2}}, } to := UserWithDetails{} copier.CopyWithOption(&to, from, optionsDeepCopy) checkDetail(t, from.Detail, to.Detail) *to.Detail.Info2 = "new value" if *from.Detail.Info2 == *to.Detail.Info2 { t.Fatalf("DeepCopy enabled") } if len(to.Details) != len(to.Details) { t.Fatalf("slice should be copied") } for idx, detail := range from.Details { checkDetail(t, detail, to.Details[idx]) } }) t.Run("Should work with different type and both ptr field", func(t *testing.T) { info2 := "world" from := UserWithDetailsPtr{Detail: &Detail{Info1: "hello", Info2: &info2}} to := EmployeeWithDetailsPtr{} copier.CopyWithOption(&to, from, optionsDeepCopy) newValue := "new value" to.Detail.Info2 = &newValue if to.Detail.Info1 == "" { t.Errorf("should not be empty") } if to.Detail.Info1 != from.Detail.Info1 { t.Errorf("should be the same") } if to.Detail.Info2 == from.Detail.Info2 { t.Errorf("should be different") } }) t.Run("Should work with different type and both not ptr field", func(t *testing.T) { info2 := "world" from := UserWithDetails{Detail: Detail{Info1: "hello", Info2: &info2}} to := EmployeeWithDetails{} copier.CopyWithOption(&to, from, optionsDeepCopy) newValue := "new value" to.Detail.Info2 = &newValue if to.Detail.Info1 == "" { t.Errorf("should not be empty") } if to.Detail.Info1 != from.Detail.Info1 { t.Errorf("should be the same") } if to.Detail.Info2 == from.Detail.Info2 { t.Errorf("should be different") } }) t.Run("Should work with from ptr field and to not ptr field", func(t *testing.T) { info2 := "world" from := UserWithDetailsPtr{Detail: &Detail{Info1: "hello", Info2: &info2}} to := EmployeeWithDetails{} copier.CopyWithOption(&to, from, optionsDeepCopy) newValue := "new value" to.Detail.Info2 = &newValue if to.Detail.Info1 == "" { t.Errorf("should not be empty") } if to.Detail.Info1 != from.Detail.Info1 { t.Errorf("should be the same") } if to.Detail.Info2 == from.Detail.Info2 { t.Errorf("should be different") } }) t.Run("Should work with from not ptr field and to ptr field", func(t *testing.T) { info2 := "world" from := UserWithDetails{Detail: Detail{Info1: "hello", Info2: &info2}} to := EmployeeWithDetailsPtr{} copier.CopyWithOption(&to, from, optionsDeepCopy) newValue := "new value" to.Detail.Info2 = &newValue if to.Detail.Info1 == "" { t.Errorf("should not be empty") } if to.Detail.Info1 != from.Detail.Info1 { t.Errorf("should be the same") } if to.Detail.Info2 == from.Detail.Info2 { t.Errorf("should be different") } }) }) } func TestMapInterface(t *testing.T) { type Inner struct { IntPtr *int unexportedField string } type Outer struct { Inner Inner } type DriverOptions struct { GenOptions map[string]interface{} } t.Run("Should work without deepCopy", func(t *testing.T) { intVal := 5 outer := Outer{ Inner: Inner{ IntPtr: &intVal, unexportedField: "hello", }, } from := DriverOptions{ GenOptions: map[string]interface{}{ "key": outer, }, } to := DriverOptions{} if err := copier.Copy(&to, &from); nil != err { t.Errorf("Unexpected error: %v", err) return } *to.GenOptions["key"].(Outer).Inner.IntPtr = 6 if to.GenOptions["key"].(Outer).Inner.IntPtr != from.GenOptions["key"].(Outer).Inner.IntPtr { t.Errorf("should be the same") } }) t.Run("Should work with deepCopy", func(t *testing.T) { intVal := 5 outer := Outer{ Inner: Inner{ IntPtr: &intVal, unexportedField: "Hello", }, } from := DriverOptions{ GenOptions: map[string]interface{}{ "key": outer, }, } to := DriverOptions{} if err := copier.CopyWithOption(&to, &from, copier.Option{ DeepCopy: true, }); nil != err { t.Errorf("Unexpected error: %v", err) return } *to.GenOptions["key"].(Outer).Inner.IntPtr = 6 if to.GenOptions["key"].(Outer).Inner.IntPtr == from.GenOptions["key"].(Outer).Inner.IntPtr { t.Errorf("should be different") } }) t.Run("Test copy map with nil interface", func(t *testing.T) { from := map[string]interface{}{"eventId": nil} to := map[string]interface{}{"eventId": nil} copier.CopyWithOption(&to, &from, copier.Option{IgnoreEmpty: true, DeepCopy: true}) if v, ok := to["eventId"]; !ok || v != nil { t.Errorf("failed to deep copy map with nil, got %v", v) } from["eventId"] = 1 if v, ok := to["eventId"]; !ok || v != nil { t.Errorf("failed to deep copy map with nil, got %v", v) } copier.CopyWithOption(&to, &from, copier.Option{IgnoreEmpty: true, DeepCopy: true}) if v, ok := to["eventId"]; !ok || v != 1 { t.Errorf("failed to deep copy map with nil") } from["eventId"] = 2 if v, ok := to["eventId"]; !ok || v != 1 { t.Errorf("failed to deep copy map with nil") } }) t.Run("Test copy map with nested slice map", func(t *testing.T) { var out map[string]interface{} var value = map[string]interface{}{ "list": []map[string]interface{}{ { "shop_id": 123, }, }, "list2": []interface{}{ map[string]interface{}{ "shop_id": 123, }, }, } err := copier.CopyWithOption(&out, &value, copier.Option{IgnoreEmpty: false, DeepCopy: true}) if err != nil { t.Fatalf("failed to deep copy nested map") } if fmt.Sprintf("%v", out) != fmt.Sprintf("%v", value) { t.Fatalf("failed to deep copy nested map") } }) } func TestInterface(t *testing.T) { type Inner struct { IntPtr *int } type Outer struct { Inner Inner } type DriverOptions struct { GenOptions interface{} } t.Run("Should work without deepCopy", func(t *testing.T) { intVal := 5 outer := Outer{ Inner: Inner{ IntPtr: &intVal, }, } from := DriverOptions{ GenOptions: outer, } to := DriverOptions{} if err := copier.Copy(&to, from); nil != err { t.Errorf("Unexpected error: %v", err) return } *to.GenOptions.(Outer).Inner.IntPtr = 6 if to.GenOptions.(Outer).Inner.IntPtr != from.GenOptions.(Outer).Inner.IntPtr { t.Errorf("should be the same") } }) t.Run("Should work with deepCopy", func(t *testing.T) { intVal := 5 outer := Outer{ Inner: Inner{ IntPtr: &intVal, }, } from := DriverOptions{ GenOptions: outer, } to := DriverOptions{} if err := copier.CopyWithOption(&to, &from, copier.Option{ DeepCopy: true, }); nil != err { t.Errorf("Unexpected error: %v", err) return } *to.GenOptions.(Outer).Inner.IntPtr = 6 if to.GenOptions.(Outer).Inner.IntPtr == from.GenOptions.(Outer).Inner.IntPtr { t.Errorf("should be different") } }) } func TestSlice(t *testing.T) { type ElemOption struct { Value int } type A struct { X []int Options []ElemOption } type B struct { X []int Options []ElemOption } t.Run("Should work with simple slice", func(t *testing.T) { from := []int{1, 2} var to []int if err := copier.Copy(&to, from); nil != err { t.Errorf("Unexpected error: %v", err) return } from[0] = 3 from[1] = 4 if to[0] == from[0] { t.Errorf("should be different") } if len(to) != len(from) { t.Errorf("should be the same length, got len(from): %v, len(to): %v", len(from), len(to)) } }) t.Run("Should work with empty slice", func(t *testing.T) { from := []int{} to := []int{} if err := copier.Copy(&to, from); nil != err { t.Errorf("Unexpected error: %v", err) return } if to == nil { t.Errorf("should be not nil") } }) t.Run("Should work without deepCopy", func(t *testing.T) { x := []int{1, 2} options := []ElemOption{ {Value: 10}, {Value: 20}, } from := A{X: x, Options: options} to := B{} if err := copier.Copy(&to, from); nil != err { t.Errorf("Unexpected error: %v", err) return } from.X[0] = 3 from.X[1] = 4 from.Options[0].Value = 30 from.Options[1].Value = 40 if to.X[0] != from.X[0] { t.Errorf("should be the same") } if len(to.X) != len(from.X) { t.Errorf("should be the same length, got len(from.X): %v, len(to.X): %v", len(from.X), len(to.X)) } if to.Options[0].Value != from.Options[0].Value { t.Errorf("should be the same") } if to.Options[0].Value != from.Options[0].Value { t.Errorf("should be the same") } if len(to.Options) != len(from.Options) { t.Errorf("should be the same") } }) t.Run("Should work with deepCopy", func(t *testing.T) { x := []int{1, 2} options := []ElemOption{ {Value: 10}, {Value: 20}, } from := A{X: x, Options: options} to := B{} if err := copier.CopyWithOption(&to, from, copier.Option{ DeepCopy: true, }); nil != err { t.Errorf("Unexpected error: %v", err) return } from.X[0] = 3 from.X[1] = 4 from.Options[0].Value = 30 from.Options[1].Value = 40 if to.X[0] == from.X[0] { t.Errorf("should be different") } if len(to.X) != len(from.X) { t.Errorf("should be the same length, got len(from.X): %v, len(to.X): %v", len(from.X), len(to.X)) } if to.Options[0].Value == from.Options[0].Value { t.Errorf("should be different") } if len(to.Options) != len(from.Options) { t.Errorf("should be the same") } }) } func TestAnonymousFields(t *testing.T) { t.Run("Should work with unexported ptr fields", func(t *testing.T) { type nested struct { A string } type parentA struct { *nested } type parentB struct { *nested } from := parentA{nested: &nested{A: "a"}} to := parentB{} err := copier.CopyWithOption(&to, &from, copier.Option{ DeepCopy: true, }) if err != nil { t.Errorf("Unexpected error: %v", err) return } from.nested.A = "b" if to.nested != nil { t.Errorf("should be nil") } }) t.Run("Should work with unexported fields", func(t *testing.T) { type nested struct { A string } type parentA struct { nested } type parentB struct { nested } from := parentA{nested: nested{A: "a"}} to := parentB{} err := copier.CopyWithOption(&to, &from, copier.Option{ DeepCopy: true, }) if err != nil { t.Errorf("Unexpected error: %v", err) return } from.nested.A = "b" if to.nested.A == from.nested.A { t.Errorf("should be different") } }) t.Run("Should work with exported ptr fields", func(t *testing.T) { type Nested struct { A string } type parentA struct { *Nested } type parentB struct { *Nested } fieldValue := "a" from := parentA{Nested: &Nested{A: fieldValue}} to := parentB{} err := copier.CopyWithOption(&to, &from, copier.Option{ DeepCopy: true, }) if err != nil { t.Errorf("Unexpected error: %v", err) return } from.Nested.A = "b" if to.Nested.A != fieldValue { t.Errorf("should not change") } }) t.Run("Should work with exported fields", func(t *testing.T) { type Nested struct { A string } type parentA struct { Nested } type parentB struct { Nested } fieldValue := "a" from := parentA{Nested: Nested{A: fieldValue}} to := parentB{} err := copier.CopyWithOption(&to, &from, copier.Option{ DeepCopy: true, }) if err != nil { t.Errorf("Unexpected error: %v", err) return } from.Nested.A = "b" if to.Nested.A != fieldValue { t.Errorf("should not change") } }) } type someStruct struct { IntField int UIntField uint64 } type structSameName1 struct { A string B int64 C time.Time D string E *someStruct } type structSameName2 struct { A string B time.Time C int64 D string E *someStruct } func TestCopyFieldsWithSameNameButDifferentTypes(t *testing.T) { obj1 := structSameName1{A: "123", B: 2, C: time.Now()} obj2 := &structSameName2{} err := copier.Copy(obj2, &obj1) if err != nil { t.Error("Should not raise error") } if obj2.A != obj1.A { t.Errorf("Field A should be copied") } } type Foo1 struct { Name string Age int32 } type Foo2 struct { Name string } type StructWithMap1 struct { Map map[int]Foo1 } type StructWithMap2 struct { Map map[int32]Foo2 } func TestCopyMapOfStruct(t *testing.T) { obj1 := StructWithMap1{Map: map[int]Foo1{2: {Name: "A pure foo"}}} obj2 := &StructWithMap2{} err := copier.Copy(obj2, obj1) if err != nil { t.Error("Should not raise error") } for k, v1 := range obj1.Map { v2, ok := obj2.Map[int32(k)] if !ok || v1.Name != v2.Name { t.Errorf("Map should be copied") } } } func TestCopyMapOfInt(t *testing.T) { map1 := map[int]int{3: 6, 4: 8} map2 := map[int32]int8{} err := copier.Copy(&map2, map1) if err != nil { t.Error("Should not raise error") } for k, v1 := range map1 { v2, ok := map2[int32(k)] if !ok || v1 != int(v2) { t.Errorf("Map should be copied") } } } func TestCopyWithOption(t *testing.T) { from := structSameName2{D: "456", E: &someStruct{IntField: 100, UIntField: 1000}} to := &structSameName1{A: "123", B: 2, C: time.Now(), D: "123", E: &someStruct{UIntField: 5000}} if err := copier.CopyWithOption(to, &from, copier.Option{IgnoreEmpty: true}); err != nil { t.Error("Should not raise error") } if to.A == from.A { t.Errorf("Field A should not be copied") } else if to.D != from.D { t.Errorf("Field D should be copied") } } type ScannerValue struct { V int } func (s *ScannerValue) Scan(src interface{}) error { return errors.New("I failed") } type ScannerStruct struct { V *ScannerValue } type ScannerStructTo struct { V *ScannerValue } func TestScanner(t *testing.T) { s := &ScannerStruct{ V: &ScannerValue{ V: 12, }, } s2 := &ScannerStructTo{} err := copier.Copy(s2, s) if err != nil { t.Error("Should not raise error") } if s.V.V != s2.V.V { t.Errorf("Field V should be copied") } } func TestScanFromPtrToSqlNullable(t *testing.T) { var ( from struct { S string Sptr *string T1 sql.NullTime T2 sql.NullTime T3 *time.Time } to struct { S sql.NullString Sptr sql.NullString T1 time.Time T2 *time.Time T3 sql.NullTime } s string err error ) s = "test" from.S = s from.Sptr = &s if from.T1.Valid || from.T2.Valid { t.Errorf("Must be not valid") } err = copier.Copy(&to, from) if err != nil { t.Error("Should not raise error") } if !to.T1.IsZero() { t.Errorf("to.T1 should be Zero but %v", to.T1) } if to.T2 != nil && !to.T2.IsZero() { t.Errorf("to.T2 should be Zero but %v", to.T2) } now := time.Now() from.T1.Scan(now) from.T2.Scan(now) err = copier.Copy(&to, from) if err != nil { t.Error("Should not raise error") } if to.S.String != from.S { t.Errorf("Field S should be copied") } if to.Sptr.String != *from.Sptr { t.Errorf("Field Sptr should be copied") } if from.T1.Time != to.T1 { t.Errorf("Fields T1 fields should be equal") } if from.T2.Time != *to.T2 { t.Errorf("Fields T2 fields should be equal") } } func TestDeepCopyInterface(t *testing.T) { var m = make(map[string]string) m["a"] = "ccc" from := []interface{}{[]int{7, 8, 9}, 2, 3, m, errors.New("aaaa")} var to []interface{} copier.CopyWithOption(&to, &from, copier.Option{ IgnoreEmpty: false, DeepCopy: true, }) from[0].([]int)[0] = 10 from[1] = "3" from[3].(map[string]string)["a"] = "bbb" if fmt.Sprint(to[0]) != fmt.Sprint([]int{7, 8, 9}) { t.Errorf("to value failed to be deep copied") } if fmt.Sprint(to[1]) != "2" { t.Errorf("to value failed to be deep copied") } if to[3].(map[string]string)["a"] != "ccc" { t.Errorf("to value failed to be deep copied") } } copier-0.3.2/errors.go000066400000000000000000000004611405510164000146350ustar00rootroot00000000000000package copier import "errors" var ( ErrInvalidCopyDestination = errors.New("copy destination is invalid") ErrInvalidCopyFrom = errors.New("copy from is invalid") ErrMapKeyNotMatch = errors.New("map's key type doesn't match") ErrNotSupported = errors.New("not supported") ) copier-0.3.2/go.mod000066400000000000000000000000511405510164000140730ustar00rootroot00000000000000module github.com/jinzhu/copier go 1.15