1package awsutil
2
3import (
4	"io"
5	"reflect"
6	"time"
7)
8
9// Copy deeply copies a src structure to dst. Useful for copying request and
10// response structures.
11//
12// Can copy between structs of different type, but will only copy fields which
13// are assignable, and exist in both structs. Fields which are not assignable,
14// or do not exist in both structs are ignored.
15func Copy(dst, src interface{}) {
16	dstval := reflect.ValueOf(dst)
17	if !dstval.IsValid() {
18		panic("Copy dst cannot be nil")
19	}
20
21	rcopy(dstval, reflect.ValueOf(src), true)
22}
23
24// CopyOf returns a copy of src while also allocating the memory for dst.
25// src must be a pointer type or this operation will fail.
26func CopyOf(src interface{}) (dst interface{}) {
27	dsti := reflect.New(reflect.TypeOf(src).Elem())
28	dst = dsti.Interface()
29	rcopy(dsti, reflect.ValueOf(src), true)
30	return
31}
32
33// rcopy performs a recursive copy of values from the source to destination.
34//
35// root is used to skip certain aspects of the copy which are not valid
36// for the root node of a object.
37func rcopy(dst, src reflect.Value, root bool) {
38	if !src.IsValid() {
39		return
40	}
41
42	switch src.Kind() {
43	case reflect.Ptr:
44		if _, ok := src.Interface().(io.Reader); ok {
45			if dst.Kind() == reflect.Ptr && dst.Elem().CanSet() {
46				dst.Elem().Set(src)
47			} else if dst.CanSet() {
48				dst.Set(src)
49			}
50		} else {
51			e := src.Type().Elem()
52			if dst.CanSet() && !src.IsNil() {
53				if _, ok := src.Interface().(*time.Time); !ok {
54					dst.Set(reflect.New(e))
55				} else {
56					tempValue := reflect.New(e)
57					tempValue.Elem().Set(src.Elem())
58					// Sets time.Time's unexported values
59					dst.Set(tempValue)
60				}
61			}
62			if src.Elem().IsValid() {
63				// Keep the current root state since the depth hasn't changed
64				rcopy(dst.Elem(), src.Elem(), root)
65			}
66		}
67	case reflect.Struct:
68		t := dst.Type()
69		for i := 0; i < t.NumField(); i++ {
70			name := t.Field(i).Name
71			srcVal := src.FieldByName(name)
72			dstVal := dst.FieldByName(name)
73			if srcVal.IsValid() && dstVal.CanSet() {
74				rcopy(dstVal, srcVal, false)
75			}
76		}
77	case reflect.Slice:
78		if src.IsNil() {
79			break
80		}
81
82		s := reflect.MakeSlice(src.Type(), src.Len(), src.Cap())
83		dst.Set(s)
84		for i := 0; i < src.Len(); i++ {
85			rcopy(dst.Index(i), src.Index(i), false)
86		}
87	case reflect.Map:
88		if src.IsNil() {
89			break
90		}
91
92		s := reflect.MakeMap(src.Type())
93		dst.Set(s)
94		for _, k := range src.MapKeys() {
95			v := src.MapIndex(k)
96			v2 := reflect.New(v.Type()).Elem()
97			rcopy(v2, v, false)
98			dst.SetMapIndex(k, v2)
99		}
100	default:
101		// Assign the value if possible. If its not assignable, the value would
102		// need to be converted and the impact of that may be unexpected, or is
103		// not compatible with the dst type.
104		if src.Type().AssignableTo(dst.Type()) {
105			dst.Set(src)
106		}
107	}
108}
109