aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/mohae/deepcopy/deepcopy.go
blob: ba763ad091e261d1e4d575dc0eb1bf4c5b9fd466 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// deepcopy makes deep copies of things. A standard copy will copy the
// pointers: deep copy copies the values pointed to.  Unexported field
// values are not copied.
//
// Copyright (c)2014-2016, Joel Scoble (github.com/mohae), all rights reserved.
// License: MIT, for more details check the included LICENSE file.
package deepcopy

import (
    "reflect"
    "time"
)

// Interface for delegating copy process to type
type Interface interface {
    DeepCopy() interface{}
}

// Iface is an alias to Copy; this exists for backwards compatibility reasons.
func Iface(iface interface{}) interface{} {
    return Copy(iface)
}

// Copy creates a deep copy of whatever is passed to it and returns the copy
// in an interface{}.  The returned value will need to be asserted to the
// correct type.
func Copy(src interface{}) interface{} {
    if src == nil {
        return nil
    }

    // Make the interface a reflect.Value
    original := reflect.ValueOf(src)

    // Make a copy of the same type as the original.
    cpy := reflect.New(original.Type()).Elem()

    // Recursively copy the original.
    copyRecursive(original, cpy)

    // Return the copy as an interface.
    return cpy.Interface()
}

// copyRecursive does the actual copying of the interface. It currently has
// limited support for what it can handle. Add as needed.
func copyRecursive(original, cpy reflect.Value) {
    // check for implement deepcopy.Interface
    if original.CanInterface() {
        if copier, ok := original.Interface().(Interface); ok {
            cpy.Set(reflect.ValueOf(copier.DeepCopy()))
            return
        }
    }

    // handle according to original's Kind
    switch original.Kind() {
    case reflect.Ptr:
        // Get the actual value being pointed to.
        originalValue := original.Elem()

        // if  it isn't valid, return.
        if !originalValue.IsValid() {
            return
        }
        cpy.Set(reflect.New(originalValue.Type()))
        copyRecursive(originalValue, cpy.Elem())

    case reflect.Interface:
        // If this is a nil, don't do anything
        if original.IsNil() {
            return
        }
        // Get the value for the interface, not the pointer.
        originalValue := original.Elem()

        // Get the value by calling Elem().
        copyValue := reflect.New(originalValue.Type()).Elem()
        copyRecursive(originalValue, copyValue)
        cpy.Set(copyValue)

    case reflect.Struct:
        t, ok := original.Interface().(time.Time)
        if ok {
            cpy.Set(reflect.ValueOf(t))
            return
        }
        // Go through each field of the struct and copy it.
        for i := 0; i < original.NumField(); i++ {
            // The Type's StructField for a given field is checked to see if StructField.PkgPath
            // is set to determine if the field is exported or not because CanSet() returns false
            // for settable fields.  I'm not sure why.  -mohae
            if original.Type().Field(i).PkgPath != "" {
                continue
            }
            copyRecursive(original.Field(i), cpy.Field(i))
        }

    case reflect.Slice:
        if original.IsNil() {
            return
        }
        // Make a new slice and copy each element.
        cpy.Set(reflect.MakeSlice(original.Type(), original.Len(), original.Cap()))
        for i := 0; i < original.Len(); i++ {
            copyRecursive(original.Index(i), cpy.Index(i))
        }

    case reflect.Map:
        if original.IsNil() {
            return
        }
        cpy.Set(reflect.MakeMap(original.Type()))
        for _, key := range original.MapKeys() {
            originalValue := original.MapIndex(key)
            copyValue := reflect.New(originalValue.Type()).Elem()
            copyRecursive(originalValue, copyValue)
            copyKey := Copy(key.Interface())
            cpy.SetMapIndex(reflect.ValueOf(copyKey), copyValue)
        }

    default:
        cpy.Set(original)
    }
}