New Upstream Release - golang-github-jinzhu-copier

Ready changes

Summary

Merged new upstream version: 0.3.5 (was: 0.3.2).

Resulting package

Built on 2022-09-29T06:40 (took 3m34s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-releases golang-github-jinzhu-copier-dev

Lintian Result

Diff

diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 2aa6f61..774b29c 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -12,7 +12,7 @@ jobs:
   ci:
     strategy:
       matrix:
-        go: ['1.15', '1.14', '1.13']
+        go: ['1.16', '1.17']
         platform: [ubuntu-latest, macos-latest] # can not run in windows OS
     runs-on: ${{ matrix.platform }}
 
diff --git a/copier.go b/copier.go
index 412ff54..6dc9600 100644
--- a/copier.go
+++ b/copier.go
@@ -24,6 +24,13 @@ const (
 
 	// Denotes that the value as been copied
 	hasCopied
+
+	// Some default converter types for a nicer syntax
+	String  string  = ""
+	Bool    bool    = false
+	Int     int     = 0
+	Float32 float32 = 0
+	Float64 float64 = 0
 )
 
 // Option sets copy options
@@ -32,6 +39,18 @@ type Option struct {
 	// struct having all it's fields set to their zero values respectively (see IsZero() in reflect/value.go)
 	IgnoreEmpty bool
 	DeepCopy    bool
+	Converters  []TypeConverter
+}
+
+type TypeConverter struct {
+	SrcType interface{}
+	DstType interface{}
+	Fn      func(src interface{}) (interface{}, error)
+}
+
+type converterPair struct {
+	SrcType reflect.Type
+	DstType reflect.Type
 }
 
 // Tag Flags
@@ -59,12 +78,27 @@ func CopyWithOption(toValue interface{}, fromValue interface{}, opt Option) (err
 
 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))
+		isSlice    bool
+		amount     = 1
+		from       = indirect(reflect.ValueOf(fromValue))
+		to         = indirect(reflect.ValueOf(toValue))
+		converters map[converterPair]TypeConverter
 	)
 
+	// 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
 	}
@@ -113,13 +147,16 @@ 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) {
+			if !set(toKey, k, opt.DeepCopy, converters) {
 				return fmt.Errorf("%w map, old key: %v, new key: %v", ErrNotSupported, k.Type(), toType.Key())
 			}
 
-			elemType, _ := indirectType(toType.Elem())
+			elemType := toType.Elem()
+			if elemType.Kind() != reflect.Slice {
+				elemType, _ = indirectType(elemType)
+			}
 			toValue := indirect(reflect.New(elemType))
-			if !set(toValue, from.MapIndex(k), opt.DeepCopy) {
+			if !set(toValue, from.MapIndex(k), opt.DeepCopy, converters) {
 				if err = copier(toValue.Addr().Interface(), from.MapIndex(k).Interface(), opt); err != nil {
 					return err
 				}
@@ -148,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) {
+			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 {
@@ -203,6 +240,8 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error)
 
 		// check source
 		if source.IsValid() {
+			copyUnexportedStructFields(dest, source)
+
 			// Copy from source field to dest field or method
 			fromTypeFields := deepFields(fromType)
 			for _, field := range fromTypeFields {
@@ -249,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) {
+							if !set(toField, fromField, opt.DeepCopy, converters) {
 								if err := copier(toField.Addr().Interface(), fromField.Interface(), opt); err != nil {
 									return err
 								}
@@ -291,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)
+							set(toField, values[0], opt.DeepCopy, converters)
 						}
 					}
 				}
@@ -303,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) {
+					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 {
@@ -315,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) {
+					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 {
@@ -334,6 +373,24 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error)
 	return
 }
 
+func copyUnexportedStructFields(to, from reflect.Value) {
+	if from.Kind() != reflect.Struct || to.Kind() != reflect.Struct || !from.Type().AssignableTo(to.Type()) {
+		return
+	}
+
+	// create a shallow copy of 'to' to get all fields
+	tmp := indirect(reflect.New(to.Type()))
+	tmp.Set(from)
+
+	// revert exported fields
+	for i := 0; i < to.NumField(); i++ {
+		if tmp.Field(i).CanSet() {
+			tmp.Field(i).Set(to.Field(i))
+		}
+	}
+	to.Set(tmp)
+}
+
 func shouldIgnore(v reflect.Value, ignoreEmpty bool) bool {
 	if !ignoreEmpty {
 		return false
@@ -348,10 +405,15 @@ func deepFields(reflectType reflect.Type) []reflect.StructField {
 
 		for i := 0; i < reflectType.NumField(); i++ {
 			v := reflectType.Field(i)
-			if v.Anonymous {
-				fields = append(fields, deepFields(v.Type)...)
-			} else {
+			// PkgPath is the package path that qualifies a lower case (unexported)
+			// field name. It is empty for upper case (exported) field names.
+			// See https://golang.org/ref/spec#Uniqueness_of_identifiers
+			if v.PkgPath == "" {
 				fields = append(fields, v)
+				if v.Anonymous {
+					// also consider fields of anonymous fields as fields of the root
+					fields = append(fields, deepFields(v.Type)...)
+				}
 			}
 		}
 
@@ -376,8 +438,14 @@ func indirectType(reflectType reflect.Type) (_ reflect.Type, isPtr bool) {
 	return reflectType, isPtr
 }
 
-func set(to, from reflect.Value, deepCopy bool) bool {
+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
+		}
+
 		if to.Kind() == reflect.Ptr {
 			// set `to` to nil if from is nil
 			if from.Kind() == reflect.Ptr && from.IsNil() {
@@ -411,6 +479,9 @@ func set(to, from reflect.Value, deepCopy bool) bool {
 					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
 			}
@@ -452,7 +523,7 @@ func set(to, from reflect.Value, deepCopy bool) bool {
 				to.Set(rv)
 			}
 		} else if from.Kind() == reflect.Ptr {
-			return set(to, from.Elem(), deepCopy)
+			return set(to, from.Elem(), deepCopy, converters)
 		} else {
 			return false
 		}
@@ -461,6 +532,33 @@ func set(to, from reflect.Value, deepCopy bool) bool {
 	return true
 }
 
+// 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) {
+	pair := converterPair{
+		SrcType: from.Type(),
+		DstType: to.Type(),
+	}
+
+	if cnv, ok := converters[pair]; ok {
+		result, err := cnv.Fn(from.Interface())
+
+		if err != nil {
+			return false, err
+		}
+
+		if result != nil {
+			to.Set(reflect.ValueOf(result))
+		} else {
+			// in case we've got a nil value to copy
+			to.Set(reflect.Zero(to.Type()))
+		}
+
+		return true, nil
+	}
+
+	return false, nil
+}
+
 // 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, ",") {
diff --git a/copier_converter_test.go b/copier_converter_test.go
new file mode 100644
index 0000000..08bd038
--- /dev/null
+++ b/copier_converter_test.go
@@ -0,0 +1,187 @@
+package copier_test
+
+import (
+	"errors"
+	"strconv"
+	"testing"
+	"time"
+
+	"github.com/jinzhu/copier"
+)
+
+func TestCopyWithTypeConverters(t *testing.T) {
+	type SrcStruct struct {
+		Field1 time.Time
+		Field2 *time.Time
+		Field3 *time.Time
+		Field4 string
+	}
+
+	type DestStruct struct {
+		Field1 string
+		Field2 string
+		Field3 string
+		Field4 int
+	}
+
+	testTime := time.Date(2021, 3, 5, 1, 30, 0, 123000000, time.UTC)
+
+	src := SrcStruct{
+		Field1: testTime,
+		Field2: &testTime,
+		Field3: nil,
+		Field4: "9000",
+	}
+
+	var dst DestStruct
+
+	err := copier.CopyWithOption(&dst, &src, copier.Option{
+		IgnoreEmpty: true,
+		DeepCopy:    true,
+		Converters: []copier.TypeConverter{
+			{
+				SrcType: time.Time{},
+				DstType: copier.String,
+				Fn: func(src interface{}) (interface{}, error) {
+					s, ok := src.(time.Time)
+
+					if !ok {
+						return nil, errors.New("src type not matching")
+					}
+
+					return s.Format(time.RFC3339), nil
+				},
+			},
+			{
+				SrcType: copier.String,
+				DstType: copier.Int,
+				Fn: func(src interface{}) (interface{}, error) {
+					s, ok := src.(string)
+
+					if !ok {
+						return nil, errors.New("src type not matching")
+					}
+
+					return strconv.Atoi(s)
+				},
+			},
+		},
+	})
+
+	if err != nil {
+		t.Fatalf(`Should be able to copy from src to dst object. %v`, err)
+		return
+	}
+
+	dateStr := "2021-03-05T01:30:00Z"
+
+	if dst.Field1 != dateStr {
+		t.Fatalf("got %q, wanted %q", dst.Field1, dateStr)
+	}
+
+	if dst.Field2 != dateStr {
+		t.Fatalf("got %q, wanted %q", dst.Field2, dateStr)
+	}
+
+	if dst.Field3 != "" {
+		t.Fatalf("got %q, wanted %q", dst.Field3, "")
+	}
+
+	if dst.Field4 != 9000 {
+		t.Fatalf("got %q, wanted %q", dst.Field4, 9000)
+	}
+}
+
+func TestCopyWithConverterAndAnnotation(t *testing.T) {
+	type SrcStruct struct {
+		Field1 string
+	}
+
+	type DestStruct struct {
+		Field1 string
+		Field2 string `copier:"Field1"`
+	}
+
+	src := SrcStruct{
+		Field1: "test",
+	}
+
+	var dst DestStruct
+
+	err := copier.CopyWithOption(&dst, &src, copier.Option{
+		IgnoreEmpty: true,
+		DeepCopy:    true,
+		Converters: []copier.TypeConverter{
+			{
+				SrcType: copier.String,
+				DstType: copier.String,
+				Fn: func(src interface{}) (interface{}, error) {
+					s, ok := src.(string)
+
+					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.Field2 != "test2" {
+		t.Fatalf("got %q, wanted %q", dst.Field2, "test2")
+	}
+}
+
+func TestCopyWithConverterStrToStrPointer(t *testing.T) {
+	type SrcStruct struct {
+		Field1 string
+	}
+
+	type DestStruct struct {
+		Field1 *string
+	}
+
+	src := SrcStruct{
+		Field1: "",
+	}
+
+	var dst DestStruct
+
+	ptrStrType := ""
+
+	err := copier.CopyWithOption(&dst, &src, copier.Option{
+		IgnoreEmpty: true,
+		DeepCopy:    true,
+		Converters: []copier.TypeConverter{
+			{
+				SrcType: copier.String,
+				DstType: &ptrStrType,
+				Fn: func(src interface{}) (interface{}, error) {
+					s, _ := src.(string)
+
+					// return nil on empty string
+					if s == "" {
+						return nil, nil
+					}
+
+					return &s, nil
+				},
+			},
+		},
+	})
+
+	if err != nil {
+		t.Fatalf(`Should be able to copy from src to dst object. %v`, err)
+		return
+	}
+
+	if dst.Field1 != nil {
+		t.Fatalf("got %q, wanted nil", *dst.Field1)
+	}
+}
diff --git a/copier_test.go b/copier_test.go
index 9450d8e..f6d68a4 100644
--- a/copier_test.go
+++ b/copier_test.go
@@ -4,6 +4,7 @@ import (
 	"database/sql"
 	"errors"
 	"fmt"
+	"reflect"
 	"testing"
 	"time"
 
@@ -26,6 +27,7 @@ func (user User) DoubleAge() int32 {
 }
 
 type Employee struct {
+	_User     *User
 	Name      string
 	Birthday  *time.Time
 	Nickname  *string
@@ -134,6 +136,10 @@ func TestCopyStruct(t *testing.T) {
 	employee4 := &Employee{}
 	copier.Copy(&employee4, user)
 	checkEmployee(*employee4, user, t, "Copy From Ptr To Double Ptr")
+
+	employee5 := &Employee{}
+	copier.Copy(&employee5, &employee)
+	checkEmployee(*employee5, user, t, "Copy From Employee To Employee")
 }
 
 func TestCopyFromStructToSlice(t *testing.T) {
@@ -328,10 +334,14 @@ func TestStructField(t *testing.T) {
 	type UserWithDetailsPtr struct {
 		Details []*Detail
 		Detail  *Detail
+		Notes   *[]string
+		Notes2  *[]string
 	}
 	type UserWithDetails struct {
 		Details []Detail
 		Detail  Detail
+		Notes   []string
+		Notes2  []string
 	}
 	type UserWithSimilarDetailsPtr struct {
 		Detail *SimilarDetail
@@ -383,7 +393,7 @@ func TestStructField(t *testing.T) {
 				t.Fatalf("DeepCopy not enabled")
 			}
 
-			if len(to.Details) != len(to.Details) {
+			if len(from.Details) != len(to.Details) {
 				t.Fatalf("slice should be copied")
 			}
 
@@ -408,7 +418,7 @@ func TestStructField(t *testing.T) {
 				t.Fatalf("DeepCopy not enabled")
 			}
 
-			if len(to.Details) != len(to.Details) {
+			if len(from.Details) != len(to.Details) {
 				t.Fatalf("slice should be copied")
 			}
 
@@ -496,6 +506,27 @@ func TestStructField(t *testing.T) {
 				t.Errorf("should be different")
 			}
 		})
+
+		t.Run("Should work with from a nil ptr slice field to a slice field", func(t *testing.T) {
+			notes := []string{"hello", "world"}
+			from := UserWithDetailsPtr{Notes: &notes, Notes2: nil}
+			to := UserWithDetails{}
+			err := copier.Copy(&to, from)
+			if err != nil {
+				t.Errorf("should not return an error")
+				return
+			}
+
+			if len(to.Notes) != len(*from.Notes) {
+				t.Errorf("should be the same length")
+			}
+			if to.Notes[0] != (*from.Notes)[0] {
+				t.Errorf("should be the same")
+			}
+			if to.Notes[1] != (*from.Notes)[1] {
+				t.Errorf("should be the same")
+			}
+		})
 	})
 
 	t.Run("Should work with deepCopy", func(t *testing.T) {
@@ -515,7 +546,7 @@ func TestStructField(t *testing.T) {
 				t.Fatalf("DeepCopy enabled")
 			}
 
-			if len(to.Details) != len(to.Details) {
+			if len(from.Details) != len(to.Details) {
 				t.Fatalf("slice should be copied")
 			}
 
@@ -539,7 +570,7 @@ func TestStructField(t *testing.T) {
 				t.Fatalf("DeepCopy enabled")
 			}
 
-			if len(to.Details) != len(to.Details) {
+			if len(from.Details) != len(to.Details) {
 				t.Fatalf("slice should be copied")
 			}
 
@@ -627,6 +658,37 @@ func TestStructField(t *testing.T) {
 				t.Errorf("should be different")
 			}
 		})
+
+		t.Run("Should work with from a nil ptr slice field to a slice field", func(t *testing.T) {
+			notes := []string{"hello", "world"}
+			from := UserWithDetailsPtr{Notes: &notes, Notes2: nil}
+			to := UserWithDetails{}
+			err := copier.CopyWithOption(&to, from, optionsDeepCopy)
+			if err != nil {
+				t.Errorf("should not return an error")
+				return
+			}
+
+			if len(to.Notes) != len(*from.Notes) {
+				t.Errorf("should be the same length")
+			}
+			if to.Notes[0] != (*from.Notes)[0] {
+				t.Errorf("should be the same")
+			}
+			if to.Notes[1] != (*from.Notes)[1] {
+				t.Errorf("should be the same")
+			}
+
+			newValue := []string{"new", "value"}
+			to.Notes = newValue
+
+			if to.Notes[0] == (*from.Notes)[0] {
+				t.Errorf("should be different")
+			}
+			if to.Notes[1] == (*from.Notes)[1] {
+				t.Errorf("should be different")
+			}
+		})
 	})
 }
 
@@ -1138,6 +1200,107 @@ func TestCopyMapOfInt(t *testing.T) {
 	}
 }
 
+func TestCopyMapOfSliceValue(t *testing.T) {
+	// case1: map's value is a simple slice
+	key, value := 2, 3
+	src := map[int][]int{key: []int{value} }
+	dst1 := map[int][]int{}
+	var dst2 map[int][]int
+	err := copier.Copy(&dst1, src)
+	if err != nil {
+		t.Error("Should not raise error")
+	}
+	err = copier.Copy(&dst2, src)
+	if err != nil {
+		t.Error("Should not raise error")
+	}
+
+	for k, v1 := range src {
+		v2, ok := dst1[k]
+		if !ok || len(v1) != len(v2) || k != key {
+			t.Errorf("Map should be copied")
+		}
+		for i, _ := range v1 {
+			if v2[i] != value {
+				t.Errorf("Map's slice value shoud be copied")
+			}
+		}
+
+		v3, ok := dst2[k]
+		if !ok || len(v1) != len(v3) {
+			t.Errorf("Map should be copied")
+		}
+		for i := range v1 {
+			if v3[i] != value {
+				t.Errorf("Map's slice value shoud be copied")
+			}
+		}
+	}
+
+	// case2: map's value is a slice whose element is map
+	key1, key2 := 2, 3
+	value = 4
+	s := map[int][]map[int]int{key1: []map[int]int{ {key2: value} } }
+	d1 := map[int][]map[int]int{key1: []map[int]int{ {key1: key2 } } }
+	d2 := map[int][]map[int]int{key1: []map[int]int{ } }
+	d3 := map[int][]map[int]int{key1:  nil }
+	d4 := map[int][]map[int]int{}
+	d5 := map[int][]map[int]int(nil)
+	ms := []map[int][]map[int]int{d1, d2, d3, d4, d5}
+	for i := range ms {
+		copier.CopyWithOption(&ms[i], s, copier.Option{IgnoreEmpty: false, DeepCopy: true})
+
+		if len(ms[i]) != len(s) {
+			t.Errorf("Number of map's keys should be equal")
+		}
+		for k, sliceMap := range ms[i] {
+			if k != key1 {
+				t.Errorf("Map's key should be copied")
+			}
+			if len(sliceMap) != len(s[key1]) || len(sliceMap) != 1 {
+				t.Errorf("Map's slice value should be copied")
+			}
+			m := sliceMap[0]
+			if len(m) != len(s[key1][0]) || len(m) != 1 {
+				t.Errorf("Map's slice value should be copied recursively")
+			}
+			for k, v := range m {
+				if k != key2 || v != value {
+					t.Errorf("Map's slice value should be copied recursively")
+				} 
+			}
+		}
+	}
+}
+
+func TestCopyMapOfPtrValue(t *testing.T) {
+	intV := 3
+	intv := intV
+	src := map[int]*int{2: &intv }
+	dst1 := map[int]*int{}
+	var dst2 map[int]*int
+	err := copier.Copy(&dst1, src)
+	if err != nil {
+		t.Error("Should not raise error")
+	}
+	err = copier.Copy(&dst2, src)
+	if err != nil {
+		t.Error("Should not raise error")
+	}
+
+	for k, v1 := range src {
+		v2, ok := dst1[k]
+		if !ok || v2 == nil || v1 == nil || *v2 != *v1 || *v2 != intV {
+			t.Errorf("Map should be copied")
+		}
+
+		v3, ok := dst2[k]
+		if !ok || v3 == nil ||*v3 != *v1 || *v3 != intV {
+			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}}
@@ -1287,3 +1450,180 @@ func TestDeepCopyInterface(t *testing.T) {
 		t.Errorf("to value failed to be deep copied")
 	}
 }
+
+func TestDeepCopyTime(t *testing.T) {
+	type embedT1 struct {
+		T5 time.Time
+	}
+
+	type embedT2 struct {
+		T6 *time.Time
+	}
+
+	var (
+		from struct {
+			T1 time.Time
+			T2 *time.Time
+
+			T3 *time.Time
+			T4 time.Time
+			T5 time.Time
+			T6 time.Time
+		}
+
+		to struct {
+			T1 time.Time
+			T2 *time.Time
+
+			T3 time.Time
+			T4 *time.Time
+			embedT1
+			embedT2
+		}
+	)
+
+	t1 := time.Now()
+	from.T1 = t1
+	t2 := t1.Add(time.Second)
+	from.T2 = &t2
+	t3 := t2.Add(time.Second)
+	from.T3 = &t3
+	t4 := t3.Add(time.Second)
+	from.T4 = t4
+	t5 := t4.Add(time.Second)
+	from.T5 = t5
+	t6 := t5.Add(time.Second)
+	from.T6 = t6
+
+	err := copier.CopyWithOption(&to, from, copier.Option{DeepCopy: true})
+	if err != nil {
+		t.Error("Should not raise error")
+	}
+
+	if !to.T1.Equal(from.T1) {
+		t.Errorf("Field T1 should be copied")
+	}
+	if !to.T2.Equal(*from.T2) {
+		t.Errorf("Field T2 should be copied")
+	}
+	if !to.T3.Equal(*from.T3) {
+		t.Errorf("Field T3 should be copied")
+	}
+	if !to.T4.Equal(from.T4) {
+		t.Errorf("Field T4 should be copied")
+	}
+	if !to.T5.Equal(from.T5) {
+		t.Errorf("Field T5 should be copied")
+	}
+	if !to.T6.Equal(from.T6) {
+		t.Errorf("Field T6 should be copied")
+	}
+}
+
+
+func TestNestedPrivateData(t *testing.T) {
+	type hasPrivate struct {
+		data int
+	}
+
+	type hasMembers struct {
+		Member hasPrivate
+	}
+
+	src := hasMembers{
+		Member: hasPrivate{
+			data: 42,
+		},
+	}
+	var shallow hasMembers
+	err := copier.Copy(&shallow, &src)
+	if err != nil {
+		t.Errorf("could not complete shallow copy")
+	}
+	if !reflect.DeepEqual(&src, &shallow) {
+		t.Errorf("shallow copy faild")
+	}
+
+	var deep hasMembers
+	err = copier.CopyWithOption(&deep, &src, copier.Option{DeepCopy: true})
+	if err != nil {
+		t.Errorf("could not complete deep copy")
+	}
+	if !reflect.DeepEqual(&src, &deep) {
+		t.Errorf("deep copy faild")
+	}
+
+	if !reflect.DeepEqual(&shallow, &deep) {
+		t.Errorf("unexpected difference between shallow and deep copy")
+	}
+}
+
+
+func TestDeepMapCopyTime(t *testing.T) {
+	t1 := time.Now()
+	t2 := t1.Add(time.Second)
+	from := []map[string]interface{}{
+		{
+			"t1": t1,
+			"t2": &t2,
+		},
+	}
+	to := make([]map[string]interface{}, len(from))
+
+	err := copier.CopyWithOption(&to, from, copier.Option{DeepCopy: true})
+	if err != nil {
+		t.Error("should not error")
+	}
+	if len(to) != len(from) {
+		t.Errorf("slice should be copied")
+	}
+	if !to[0]["t1"].(time.Time).Equal(from[0]["t1"].(time.Time)) {
+		t.Errorf("nested time ptr should be copied")
+	}
+	if !to[0]["t2"].(*time.Time).Equal(*from[0]["t2"].(*time.Time)) {
+		t.Errorf("nested time ptr should be copied")
+	}
+}
+
+func TestCopySimpleTime(t *testing.T) {
+	from := time.Now()
+	to := time.Time{}
+
+	err := copier.Copy(&to, from)
+	if err != nil {
+		t.Error("should not error")
+	}
+	if !from.Equal(to) {
+		t.Errorf("to (%v) value should equal from (%v) value", to, from)
+	}
+}
+
+func TestDeepCopySimpleTime(t *testing.T) {
+	from := time.Now()
+	to := time.Time{}
+
+	err := copier.CopyWithOption(&to, from, copier.Option{DeepCopy: true})
+	if err != nil {
+		t.Error("should not error")
+	}
+	if !from.Equal(to) {
+		t.Errorf("to (%v) value should equal from (%v) value", to, from)
+	}
+}
+
+type TimeWrapper struct{
+	time.Time
+}
+
+func TestDeepCopyAnonymousFieldTime(t *testing.T) {
+	from := TimeWrapper{time.Now()}
+	to := TimeWrapper{}
+
+	err := copier.CopyWithOption(&to, from, copier.Option{DeepCopy: true})
+	if err != nil {
+		t.Error("should not error")
+	}
+	if !from.Time.Equal(to.Time) {
+		t.Errorf("to (%v) value should equal from (%v) value", to.Time, from.Time)
+	}
+}
diff --git a/debian/changelog b/debian/changelog
index ce37c4d..d285572 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+golang-github-jinzhu-copier (0.3.5-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Thu, 29 Sep 2022 06:37:18 -0000
+
 golang-github-jinzhu-copier (0.3.2-2) unstable; urgency=medium
 
   * update standards version
diff --git a/go.mod b/go.mod
index 531422d..309801e 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,3 @@
 module github.com/jinzhu/copier
 
-go 1.15
+go 1.13

Debdiff

[The following lists of changes regard files as different if they have different names, permissions or owners.]

Files in second set of .debs but not in first

-rw-r--r--  root/root   /usr/share/gocode/src/github.com/jinzhu/copier/copier_converter_test.go

No differences were encountered in the control files

More details

Full run details