rewrite checkpoint

This commit is contained in:
Cory Bennett
2017-08-13 18:23:38 -07:00
parent b00021ccbd
commit 36632a52f0
883 changed files with 222856 additions and 2972 deletions
+3
View File
@@ -0,0 +1,3 @@
guard 'gotest' do
watch(%r{\.go$})
end
+20
View File
@@ -0,0 +1,20 @@
The 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.
+98
View File
@@ -0,0 +1,98 @@
# Copier
I am a copier, I copy everything from one to another
## 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
## Usage
```go
package main
import (
"fmt"
"github.com/jinzhu/copier"
)
type User struct {
Name string
Role string
Age int32
}
func (user *User) DoubleAge() int32 {
return 2 * user.Age
}
type Employee struct {
Name string
Age int32
DoubleAge int32
EmployeId int64
SuperRule string
}
func (employee *Employee) Role(role string) {
employee.SuperRule = "Super " + role
}
func main() {
var (
user = User{Name: "Jinzhu", Age: 18, Role: "Admin"}
users = []User{{Name: "Jinzhu", Age: 18, Role: "Admin"}, {Name: "jinzhu 2", Age: 30, Role: "Dev"}}
employee = Employee{}
employees = []Employee{}
)
copier.Copy(&employee, &user)
fmt.Printf("%#v \n", employee)
// Employee{
// Name: "Jinzhu", // Copy from field
// Age: 18, // Copy from field
// DoubleAge: 36, // Copy from method
// EmployeeId: 0, // Ignored
// SuperRule: "Super Admin", // Copy to method
// }
// Copy struct to slice
copier.Copy(&employees, &user)
fmt.Printf("%#v \n", employees)
// []Employee{
// {Name: "Jinzhu", Age: 18, DoubleAge: 36, EmployeId: 0, SuperRule: "Super Admin"}
// }
// Copy slice to slice
employees = []Employee{}
copier.Copy(&employees, &users)
fmt.Printf("%#v \n", employees)
// []Employee{
// {Name: "Jinzhu", Age: 18, DoubleAge: 36, EmployeId: 0, SuperRule: "Super Admin"},
// {Name: "jinzhu 2", Age: 30, DoubleAge: 60, EmployeId: 0, SuperRule: "Super Dev"},
// }
}
```
# Supporting the project
[![http://patreon.com/jinzhu](http://patreon_public_assets.s3.amazonaws.com/sized/becomeAPatronBanner.png)](http://patreon.com/jinzhu)
# Author
**jinzhu**
* <http://github.com/jinzhu>
* <wosmvp@gmail.com>
* <http://twitter.com/zhangjinzhu>
## License
Released under the [MIT License](https://github.com/jinzhu/copier/blob/master/License).
+174
View File
@@ -0,0 +1,174 @@
package copier
import (
"database/sql"
"errors"
"reflect"
)
// Copy copy things
func Copy(toValue interface{}, fromValue interface{}) (err error) {
var (
isSlice bool
amount = 1
from = indirect(reflect.ValueOf(fromValue))
to = indirect(reflect.ValueOf(toValue))
)
if !to.CanAddr() {
return errors.New("copy to value is unaddressable")
}
// Return is from value is invalid
if !from.IsValid() {
return
}
// Just set it if possible to assign
if from.Type().AssignableTo(to.Type()) {
to.Set(from)
return
}
fromType := indirectType(from.Type())
toType := indirectType(to.Type())
if 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)
}
// Copy from field to field or method
for _, field := range deepFields(fromType) {
name := field.Name
if fromField := source.FieldByName(name); fromField.IsValid() {
// has field
if toField := dest.FieldByName(name); toField.IsValid() {
if toField.CanSet() {
if !set(toField, fromField) {
if err := Copy(toField.Addr().Interface(), fromField.Interface()); err != nil {
return err
}
}
}
} else {
// try to set to method
var toMethod reflect.Value
if dest.CanAddr() {
toMethod = dest.Addr().MethodByName(name)
} else {
toMethod = dest.MethodByName(name)
}
if toMethod.IsValid() && toMethod.Type().NumIn() == 1 && fromField.Type().AssignableTo(toMethod.Type().In(0)) {
toMethod.Call([]reflect.Value{fromField})
}
}
}
}
// Copy from method to field
for _, field := range deepFields(toType) {
name := field.Name
var fromMethod reflect.Value
if source.CanAddr() {
fromMethod = source.Addr().MethodByName(name)
} else {
fromMethod = source.MethodByName(name)
}
if fromMethod.IsValid() && fromMethod.Type().NumIn() == 0 && fromMethod.Type().NumOut() == 1 {
if toField := dest.FieldByName(name); toField.IsValid() && toField.CanSet() {
values := fromMethod.Call([]reflect.Value{})
if len(values) >= 1 {
set(toField, values[0])
}
}
}
}
if isSlice {
if dest.Addr().Type().AssignableTo(to.Type().Elem()) {
to.Set(reflect.Append(to, dest.Addr()))
} else if dest.Type().AssignableTo(to.Type().Elem()) {
to.Set(reflect.Append(to, dest))
}
}
}
return
}
func deepFields(reflectType reflect.Type) []reflect.StructField {
var fields []reflect.StructField
if reflectType = indirectType(reflectType); reflectType.Kind() == reflect.Struct {
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
}
func indirect(reflectValue reflect.Value) reflect.Value {
for reflectValue.Kind() == reflect.Ptr {
reflectValue = reflectValue.Elem()
}
return reflectValue
}
func indirectType(reflectType reflect.Type) reflect.Type {
for reflectType.Kind() == reflect.Ptr || reflectType.Kind() == reflect.Slice {
reflectType = reflectType.Elem()
}
return reflectType
}
func set(to, from reflect.Value) bool {
if from.IsValid() {
if to.Kind() == reflect.Ptr {
if to.IsNil() {
to.Set(reflect.New(to.Type().Elem()))
}
to = to.Elem()
}
if from.Type().ConvertibleTo(to.Type()) {
to.Set(from.Convert(to.Type()))
} else if scanner, ok := to.Addr().Interface().(sql.Scanner); ok {
scanner.Scan(from.Interface())
} else if from.Kind() == reflect.Ptr {
return set(to, from.Elem())
} else {
return false
}
}
return true
}
+45
View File
@@ -0,0 +1,45 @@
package 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 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(),
Notes: user.Notes,
}
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)
}
}
+143
View File
@@ -0,0 +1,143 @@
package 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
}
type TypeStruct2 struct {
Field1 int
Field2 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")
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)
}
}
+189
View File
@@ -0,0 +1,189 @@
package copier_test
import (
"fmt"
"reflect"
"testing"
"github.com/jinzhu/copier"
)
type User struct {
Name string
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
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.Age != int64(user.Age) {
t.Errorf("%v: Age haven't been copied correctly.", testCase)
}
if user.FakeAge != nil && employee.FakeAge != int(*user.FakeAge) {
fmt.Println(employee.FakeAge)
fmt.Println(*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 !reflect.DeepEqual(employee.Notes, user.Notes) {
t.Errorf("%v: Copy from slice doen't work", 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{User{Name: "Jinzhu", Age: 18, Role: "Admin", Notes: []string{"hello world"}}, User{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 TestEmbedded(t *testing.T) {
type Base struct {
BaseField1 int
BaseField2 int
}
type Embed struct {
EmbedField1 int
EmbedField2 int
Base
}
base := Base{}
embeded := Embed{}
embeded.BaseField1 = 1
embeded.BaseField2 = 2
embeded.EmbedField1 = 3
embeded.EmbedField2 = 4
copier.Copy(&base, &embeded)
if base.BaseField1 != 1 {
t.Error("Embedded fields not copied")
}
}