Add package for deep copying
This commit is contained in:
parent
6f9372cb66
commit
c3ecaed152
2 changed files with 320 additions and 0 deletions
87
util/deep/deep.go
Normal file
87
util/deep/deep.go
Normal file
|
@ -0,0 +1,87 @@
|
|||
// Copyright © 2016 Abcum Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package deep
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Copy returns a deep copy of the source object.
|
||||
func Copy(src interface{}) interface{} {
|
||||
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
prime := reflect.ValueOf(src)
|
||||
|
||||
clone := reflect.New(prime.Type()).Elem()
|
||||
|
||||
copy(prime, clone)
|
||||
|
||||
return clone.Interface()
|
||||
|
||||
}
|
||||
|
||||
func copy(prime, clone reflect.Value) {
|
||||
|
||||
switch prime.Kind() {
|
||||
|
||||
case reflect.Ptr:
|
||||
value := prime.Elem()
|
||||
if !value.IsValid() {
|
||||
return
|
||||
}
|
||||
clone.Set(reflect.New(value.Type()))
|
||||
copy(value, clone.Elem())
|
||||
|
||||
case reflect.Interface:
|
||||
if prime.IsNil() {
|
||||
return
|
||||
}
|
||||
value := prime.Elem()
|
||||
alike := reflect.New(value.Type()).Elem()
|
||||
copy(value, alike)
|
||||
clone.Set(alike)
|
||||
|
||||
case reflect.Struct:
|
||||
for i := 0; i < prime.NumField(); i++ {
|
||||
if prime.Type().Field(i).PkgPath != "" {
|
||||
continue
|
||||
}
|
||||
copy(prime.Field(i), clone.Field(i))
|
||||
}
|
||||
|
||||
case reflect.Slice:
|
||||
clone.Set(reflect.MakeSlice(prime.Type(), prime.Len(), prime.Cap()))
|
||||
for i := 0; i < prime.Len(); i++ {
|
||||
copy(prime.Index(i), clone.Index(i))
|
||||
}
|
||||
|
||||
case reflect.Map:
|
||||
clone.Set(reflect.MakeMap(prime.Type()))
|
||||
for _, key := range prime.MapKeys() {
|
||||
value := prime.MapIndex(key)
|
||||
alike := reflect.New(value.Type()).Elem()
|
||||
copy(value, alike)
|
||||
clone.SetMapIndex(key, alike)
|
||||
}
|
||||
|
||||
default:
|
||||
clone.Set(prime)
|
||||
|
||||
}
|
||||
|
||||
}
|
233
util/deep/deep_test.go
Normal file
233
util/deep/deep_test.go
Normal file
|
@ -0,0 +1,233 @@
|
|||
package deep
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
type Mini struct {
|
||||
embedded bool
|
||||
}
|
||||
|
||||
type Test struct {
|
||||
PublicNumber int
|
||||
PublicString string
|
||||
PublicStruct interface{}
|
||||
PublicPointr interface{}
|
||||
PublicSlice []interface{}
|
||||
PublicMap map[string]interface{}
|
||||
privateNumber int
|
||||
privateString string
|
||||
privateStruct interface{}
|
||||
privatePointr interface{}
|
||||
privateSlice []interface{}
|
||||
privateMap map[string]interface{}
|
||||
}
|
||||
|
||||
func TestMain(t *testing.T) {
|
||||
|
||||
Convey("Can copy nil", t, func() {
|
||||
var item interface{}
|
||||
item = nil
|
||||
done := Copy(item)
|
||||
So(item, ShouldResemble, done)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestSimple(t *testing.T) {
|
||||
|
||||
Convey("Can copy bool", t, func() {
|
||||
item := true
|
||||
done := Copy(item)
|
||||
So(item, ShouldResemble, done)
|
||||
})
|
||||
|
||||
Convey("Can copy int64", t, func() {
|
||||
item := int64(1)
|
||||
done := Copy(item)
|
||||
So(item, ShouldResemble, done)
|
||||
})
|
||||
|
||||
Convey("Can copy float64", t, func() {
|
||||
item := float64(1)
|
||||
done := Copy(item)
|
||||
So(item, ShouldResemble, done)
|
||||
})
|
||||
|
||||
Convey("Can copy string", t, func() {
|
||||
item := "string"
|
||||
done := Copy(item)
|
||||
So(item, ShouldResemble, done)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestSlices(t *testing.T) {
|
||||
|
||||
Convey("Can copy slice of bools", t, func() {
|
||||
item := []bool{
|
||||
true,
|
||||
false,
|
||||
}
|
||||
done := Copy(item)
|
||||
So(item, ShouldResemble, done)
|
||||
})
|
||||
|
||||
Convey("Can copy slice of int64s", t, func() {
|
||||
item := []int64{
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
}
|
||||
done := Copy(item)
|
||||
So(item, ShouldResemble, done)
|
||||
})
|
||||
|
||||
Convey("Can copy slice of float64s", t, func() {
|
||||
item := []float64{
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
}
|
||||
done := Copy(item)
|
||||
So(item, ShouldResemble, done)
|
||||
})
|
||||
|
||||
Convey("Can copy slice of strings", t, func() {
|
||||
item := []string{
|
||||
"str",
|
||||
"str",
|
||||
"str",
|
||||
}
|
||||
done := Copy(item)
|
||||
So(item, ShouldResemble, done)
|
||||
})
|
||||
|
||||
Convey("Can copy slice of interfaces", t, func() {
|
||||
item := []interface{}{
|
||||
nil,
|
||||
int64(1),
|
||||
float64(2),
|
||||
"str",
|
||||
}
|
||||
done := Copy(item)
|
||||
So(item, ShouldResemble, done)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestObjects(t *testing.T) {
|
||||
|
||||
Convey("Can copy map of bools", t, func() {
|
||||
item := map[string]bool{
|
||||
"a": true,
|
||||
"b": false,
|
||||
}
|
||||
done := Copy(item)
|
||||
So(item, ShouldResemble, done)
|
||||
})
|
||||
|
||||
Convey("Can copy map of int64s", t, func() {
|
||||
item := map[string]int64{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
}
|
||||
done := Copy(item)
|
||||
So(item, ShouldResemble, done)
|
||||
})
|
||||
|
||||
Convey("Can copy map of float64s", t, func() {
|
||||
item := map[string]float64{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
}
|
||||
done := Copy(item)
|
||||
So(item, ShouldResemble, done)
|
||||
})
|
||||
|
||||
Convey("Can copy map of strings", t, func() {
|
||||
item := map[string]string{
|
||||
"a": "str",
|
||||
"b": "str",
|
||||
"c": "str",
|
||||
}
|
||||
done := Copy(item)
|
||||
So(item, ShouldResemble, done)
|
||||
})
|
||||
|
||||
Convey("Can copy map of interfaces", t, func() {
|
||||
item := map[interface{}]interface{}{
|
||||
1: nil,
|
||||
"b": int64(1),
|
||||
true: float64(2),
|
||||
"d": "str",
|
||||
}
|
||||
done := Copy(item)
|
||||
So(item, ShouldResemble, done)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestStructs(t *testing.T) {
|
||||
|
||||
full := Test{
|
||||
PublicNumber: 1,
|
||||
PublicString: "str",
|
||||
PublicStruct: Mini{},
|
||||
PublicPointr: &Mini{},
|
||||
PublicSlice: []interface{}{nil, 1, "str", true},
|
||||
PublicMap: map[string]interface{}{
|
||||
"a": 1,
|
||||
"b": "str",
|
||||
"c": true,
|
||||
},
|
||||
privateNumber: 1,
|
||||
privateString: "str",
|
||||
privateStruct: Mini{},
|
||||
privatePointr: &Mini{},
|
||||
privateSlice: []interface{}{nil, 1, "str", true},
|
||||
privateMap: map[string]interface{}{
|
||||
"a": 1,
|
||||
"b": "str",
|
||||
"c": true,
|
||||
},
|
||||
}
|
||||
|
||||
show := Test{
|
||||
PublicNumber: full.PublicNumber,
|
||||
PublicString: full.PublicString,
|
||||
PublicStruct: full.PublicStruct,
|
||||
PublicPointr: full.PublicPointr,
|
||||
PublicSlice: full.PublicSlice,
|
||||
PublicMap: full.PublicMap,
|
||||
}
|
||||
|
||||
Convey("Can copy struct", t, func() {
|
||||
item := full
|
||||
done := Copy(item)
|
||||
So(done, ShouldResemble, show)
|
||||
})
|
||||
|
||||
Convey("Can copy pointer", t, func() {
|
||||
item := &full
|
||||
done := Copy(item)
|
||||
So(done, ShouldResemble, &show)
|
||||
})
|
||||
|
||||
Convey("Can copy slice of structs", t, func() {
|
||||
item := []interface{}{full, full}
|
||||
done := Copy(item)
|
||||
So(done, ShouldResemble, []interface{}{show, show})
|
||||
})
|
||||
|
||||
Convey("Can copy slice of pointers", t, func() {
|
||||
item := []interface{}{&full, &full}
|
||||
done := Copy(item)
|
||||
So(done, ShouldResemble, []interface{}{&show, &show})
|
||||
})
|
||||
|
||||
}
|
Loading…
Reference in a new issue