feat: custom copier library

This commit is contained in:
zhuyasen 2025-05-28 20:15:12 +08:00
parent 6a5eab8a6a
commit da3e2f3afa
28 changed files with 680 additions and 113 deletions

View File

@ -8,9 +8,8 @@ import (
"fmt"
"strconv"
"github.com/jinzhu/copier"
"github.com/go-dev-frame/sponge/pkg/conf"
"github.com/go-dev-frame/sponge/pkg/copier"
"github.com/go-dev-frame/sponge/pkg/logger"
"github.com/go-dev-frame/sponge/pkg/nacoscli"
"github.com/go-dev-frame/sponge/pkg/stat"

View File

@ -20,9 +20,13 @@ var (
func NewRootCMD() *cobra.Command {
cmd := &cobra.Command{
Use: "sponge",
Long: fmt.Sprintf(`Sponge is a powerful Go development framework, it's easy to develop web and microservice projects.
Long: fmt.Sprintf(`
A powerful and easy-to-use Go development framework that enables you to effortlessly
build stable, reliable, and high-performance backend services with a "low-code" approach.
Repo: %s
Docs: %s`, color.HiCyanString("https://github.com/go-dev-frame/sponge"), color.HiCyanString("https://go-sponge.com")),
Docs: %s`,
color.HiCyanString("https://github.com/go-dev-frame/sponge"),
color.HiCyanString("https://go-sponge.com")),
SilenceErrors: true,
SilenceUsage: true,
Version: getVersion(),

View File

@ -4,8 +4,8 @@ import (
"errors"
"github.com/gin-gonic/gin"
"github.com/jinzhu/copier"
"github.com/go-dev-frame/sponge/pkg/copier"
"github.com/go-dev-frame/sponge/pkg/gin/middleware"
"github.com/go-dev-frame/sponge/pkg/gin/response"
"github.com/go-dev-frame/sponge/pkg/logger"

View File

@ -5,8 +5,8 @@ import (
"math"
"github.com/gin-gonic/gin"
"github.com/jinzhu/copier"
"github.com/go-dev-frame/sponge/pkg/copier"
"github.com/go-dev-frame/sponge/pkg/gin/middleware"
"github.com/go-dev-frame/sponge/pkg/gin/response"
"github.com/go-dev-frame/sponge/pkg/logger"

View File

@ -5,8 +5,8 @@ import (
"math"
"github.com/gin-gonic/gin"
"github.com/jinzhu/copier"
"github.com/go-dev-frame/sponge/pkg/copier"
"github.com/go-dev-frame/sponge/pkg/gin/middleware"
"github.com/go-dev-frame/sponge/pkg/gin/response"
"github.com/go-dev-frame/sponge/pkg/logger"

View File

@ -4,8 +4,8 @@ import (
"errors"
"github.com/gin-gonic/gin"
"github.com/jinzhu/copier"
"github.com/go-dev-frame/sponge/pkg/copier"
"github.com/go-dev-frame/sponge/pkg/gin/middleware"
"github.com/go-dev-frame/sponge/pkg/gin/response"
"github.com/go-dev-frame/sponge/pkg/logger"

View File

@ -4,8 +4,8 @@ import (
"errors"
"github.com/gin-gonic/gin"
"github.com/jinzhu/copier"
"github.com/go-dev-frame/sponge/pkg/copier"
"github.com/go-dev-frame/sponge/pkg/gin/middleware"
"github.com/go-dev-frame/sponge/pkg/gin/response"
"github.com/go-dev-frame/sponge/pkg/logger"

View File

@ -4,8 +4,8 @@ import (
"errors"
"github.com/gin-gonic/gin"
"github.com/jinzhu/copier"
"github.com/go-dev-frame/sponge/pkg/copier"
"github.com/go-dev-frame/sponge/pkg/gin/middleware"
"github.com/go-dev-frame/sponge/pkg/gin/response"
"github.com/go-dev-frame/sponge/pkg/logger"

View File

@ -6,8 +6,7 @@ import (
"strings"
"time"
"github.com/jinzhu/copier"
"github.com/go-dev-frame/sponge/pkg/copier"
"github.com/go-dev-frame/sponge/pkg/gin/middleware"
"github.com/go-dev-frame/sponge/pkg/logger"
"github.com/go-dev-frame/sponge/pkg/sgorm/query"
@ -179,12 +178,7 @@ func convertUserExamplePb(record *model.UserExample) (*serverNameExampleV1.UserE
if err != nil {
return nil, err
}
// Note: if copier.Copy cannot assign a value to a field, add it here, e.g. CreatedAt, UpdatedAt
value.Id = record.ID
// todo generate the conversion createdAt and updatedAt code here
// delete the templates code start
value.CreatedAt = record.CreatedAt.Format(time.RFC3339)
value.UpdatedAt = record.UpdatedAt.Format(time.RFC3339)
// delete the templates code end
// Note: if copier.Copy cannot assign a value to a field, add it here
return value, nil
}

View File

@ -7,8 +7,7 @@ import (
"strings"
"time"
"github.com/jinzhu/copier"
"github.com/go-dev-frame/sponge/pkg/copier"
"github.com/go-dev-frame/sponge/pkg/sgorm/query"
"github.com/go-dev-frame/sponge/pkg/gin/middleware"
"github.com/go-dev-frame/sponge/pkg/logger"
@ -301,12 +300,7 @@ func convertUserExamplePb(record *model.UserExample) (*serverNameExampleV1.UserE
if err != nil {
return nil, err
}
// Note: if copier.Copy cannot assign a value to a field, add it here, e.g. CreatedAt, UpdatedAt
value.Id = record.ID
// todo generate the conversion createdAt and updatedAt code here
// delete the templates code start
value.CreatedAt = record.CreatedAt.Format(time.RFC3339)
value.UpdatedAt = record.UpdatedAt.Format(time.RFC3339)
// delete the templates code end
// Note: if copier.Copy cannot assign a value to a field, add it here
return value, nil
}

View File

@ -7,8 +7,7 @@ import (
"strings"
"time"
"github.com/jinzhu/copier"
"github.com/go-dev-frame/sponge/pkg/copier"
"github.com/go-dev-frame/sponge/pkg/sgorm/query"
"github.com/go-dev-frame/sponge/pkg/gin/middleware"
"github.com/go-dev-frame/sponge/pkg/logger"
@ -304,12 +303,7 @@ func convert{{.TableNameCamel}}Pb(record *model.{{.TableNameCamel}}) (*serverNam
if err != nil {
return nil, err
}
// Note: if copier.Copy cannot assign a value to a field, add it here, e.g. CreatedAt, UpdatedAt
value.{{if .IsStandardPrimaryKey}}Id{{else}}{{.ColumnNameCamel}}{{end}} = record.{{.ColumnNameCamel}}
// todo generate the conversion createdAt and updatedAt code here
// delete the templates code start
value.CreatedAt = record.CreatedAt.Format(time.RFC3339)
value.UpdatedAt = record.UpdatedAt.Format(time.RFC3339)
// delete the templates code end
// Note: if copier.Copy cannot assign a value to a field, add it here
return value, nil
}

View File

@ -6,8 +6,7 @@ import (
"strings"
"time"
"github.com/jinzhu/copier"
"github.com/go-dev-frame/sponge/pkg/copier"
"github.com/go-dev-frame/sponge/pkg/gin/middleware"
"github.com/go-dev-frame/sponge/pkg/logger"
"github.com/go-dev-frame/sponge/pkg/mgo/query"
@ -180,12 +179,8 @@ func convertUserExamplePb(record *model.UserExample) (*serverNameExampleV1.UserE
if err != nil {
return nil, err
}
// Note: if copier.Copy cannot assign a value to a field, add it here, e.g. CreatedAt, UpdatedAt
// Note: if copier.Copy cannot assign a value to a field, add it here
value.Id = record.ID.Hex()
// todo generate the conversion createdAt and updatedAt code here
// delete the templates code start
value.CreatedAt = record.CreatedAt.Format(time.RFC3339)
value.UpdatedAt = record.UpdatedAt.Format(time.RFC3339)
// delete the templates code end
return value, nil
}

View File

@ -6,8 +6,7 @@ import (
"strings"
"time"
"github.com/jinzhu/copier"
"github.com/go-dev-frame/sponge/pkg/copier"
"github.com/go-dev-frame/sponge/pkg/gin/middleware"
"github.com/go-dev-frame/sponge/pkg/logger"
"github.com/go-dev-frame/sponge/pkg/mgo/query"
@ -301,12 +300,8 @@ func convertUserExamplePb(record *model.UserExample) (*serverNameExampleV1.UserE
if err != nil {
return nil, err
}
// Note: if copier.Copy cannot assign a value to a field, add it here, e.g. CreatedAt, UpdatedAt
// Note: if copier.Copy cannot assign a value to a field, add it here
value.Id = record.ID.Hex()
// todo generate the conversion createdAt and updatedAt code here
// delete the templates code start
value.CreatedAt = record.CreatedAt.Format(time.RFC3339)
value.UpdatedAt = record.UpdatedAt.Format(time.RFC3339)
// delete the templates code end
return value, nil
}

View File

@ -6,8 +6,7 @@ import (
"strings"
"time"
"github.com/jinzhu/copier"
"github.com/go-dev-frame/sponge/pkg/copier"
"github.com/go-dev-frame/sponge/pkg/sgorm/query"
"github.com/go-dev-frame/sponge/pkg/gin/middleware"
"github.com/go-dev-frame/sponge/pkg/logger"
@ -179,12 +178,7 @@ func convert{{.TableNameCamel}}Pb(record *model.{{.TableNameCamel}}) (*serverNam
if err != nil {
return nil, err
}
// Note: if copier.Copy cannot assign a value to a field, add it here, e.g. CreatedAt, UpdatedAt
value.{{if .IsStandardPrimaryKey}}Id{{else}}{{.ColumnNameCamel}}{{end}} = record.{{.ColumnNameCamel}}
// todo generate the conversion createdAt and updatedAt code here
// delete the templates code start
value.CreatedAt = record.CreatedAt.Format(time.RFC3339)
value.UpdatedAt = record.UpdatedAt.Format(time.RFC3339)
// delete the templates code end
// Note: if copier.Copy cannot assign a value to a field, add it here
return value, nil
}

View File

@ -8,9 +8,9 @@ import (
"github.com/DATA-DOG/go-sqlmock"
"github.com/gin-gonic/gin"
"github.com/go-dev-frame/sponge/api/types"
"github.com/jinzhu/copier"
"github.com/stretchr/testify/assert"
"github.com/go-dev-frame/sponge/pkg/copier"
"github.com/go-dev-frame/sponge/pkg/gin/response"
"github.com/go-dev-frame/sponge/pkg/gotest"
"github.com/go-dev-frame/sponge/pkg/httpcli"

View File

@ -7,10 +7,10 @@ import (
"github.com/DATA-DOG/go-sqlmock"
"github.com/gin-gonic/gin"
"github.com/jinzhu/copier"
"github.com/stretchr/testify/assert"
"github.com/go-dev-frame/sponge/api/types"
"github.com/go-dev-frame/sponge/pkg/copier"
"github.com/go-dev-frame/sponge/pkg/gin/response"
"github.com/go-dev-frame/sponge/pkg/httpcli"
"github.com/go-dev-frame/sponge/pkg/gotest"

View File

@ -6,9 +6,9 @@ import (
"time"
"github.com/DATA-DOG/go-sqlmock"
"github.com/jinzhu/copier"
"github.com/stretchr/testify/assert"
"github.com/go-dev-frame/sponge/pkg/copier"
"github.com/go-dev-frame/sponge/pkg/gotest"
"github.com/go-dev-frame/sponge/pkg/httpcli"
"github.com/go-dev-frame/sponge/pkg/sgorm/query"

View File

@ -6,9 +6,9 @@ import (
"time"
"github.com/DATA-DOG/go-sqlmock"
"github.com/jinzhu/copier"
"github.com/stretchr/testify/assert"
"github.com/go-dev-frame/sponge/pkg/copier"
"github.com/go-dev-frame/sponge/pkg/sgorm/query"
"github.com/go-dev-frame/sponge/pkg/httpcli"
"github.com/go-dev-frame/sponge/pkg/gotest"

View File

@ -6,9 +6,9 @@ import (
"strings"
"time"
"github.com/jinzhu/copier"
"google.golang.org/grpc"
"github.com/go-dev-frame/sponge/pkg/copier"
"github.com/go-dev-frame/sponge/pkg/grpc/interceptor"
"github.com/go-dev-frame/sponge/pkg/logger"
"github.com/go-dev-frame/sponge/pkg/sgorm/query"
@ -191,12 +191,7 @@ func convertUserExample(record *model.UserExample) (*serverNameExampleV1.UserExa
if err != nil {
return nil, err
}
// Note: if copier.Copy cannot assign a value to a field, add it here, e.g. CreatedAt, UpdatedAt
value.Id = record.ID
// todo generate the conversion createdAt and updatedAt code here
// delete the templates code start
value.CreatedAt = record.CreatedAt.Format(time.RFC3339)
value.UpdatedAt = record.UpdatedAt.Format(time.RFC3339)
// delete the templates code end
// Note: if copier.Copy cannot assign a value to a field, add it here
return value, nil
}

View File

@ -7,9 +7,9 @@ import (
"strings"
"time"
"github.com/jinzhu/copier"
"google.golang.org/grpc"
"github.com/go-dev-frame/sponge/pkg/copier"
"github.com/go-dev-frame/sponge/pkg/sgorm/query"
"github.com/go-dev-frame/sponge/pkg/grpc/interceptor"
"github.com/go-dev-frame/sponge/pkg/logger"
@ -317,12 +317,7 @@ func convertUserExample(record *model.UserExample) (*serverNameExampleV1.UserExa
if err != nil {
return nil, err
}
// Note: if copier.Copy cannot assign a value to a field, add it here, e.g. CreatedAt, UpdatedAt
value.Id = record.ID
// todo generate the conversion createdAt and updatedAt code here
// delete the templates code start
value.CreatedAt = record.CreatedAt.Format(time.RFC3339)
value.UpdatedAt = record.UpdatedAt.Format(time.RFC3339)
// delete the templates code end
// Note: if copier.Copy cannot assign a value to a field, add it here
return value, nil
}

View File

@ -7,9 +7,9 @@ import (
"strings"
"time"
"github.com/jinzhu/copier"
"google.golang.org/grpc"
"github.com/go-dev-frame/sponge/pkg/copier"
"github.com/go-dev-frame/sponge/pkg/sgorm/query"
"github.com/go-dev-frame/sponge/pkg/grpc/interceptor"
"github.com/go-dev-frame/sponge/pkg/logger"
@ -320,12 +320,7 @@ func convert{{.TableNameCamel}}(record *model.{{.TableNameCamel}}) (*serverNameE
if err != nil {
return nil, err
}
// Note: if copier.Copy cannot assign a value to a field, add it here, e.g. CreatedAt, UpdatedAt
value.{{if .IsStandardPrimaryKey}}Id{{else}}{{.ColumnNameCamel}}{{end}} = record.{{.ColumnNameCamel}}
// todo generate the conversion createdAt and updatedAt code here
// delete the templates code start
value.CreatedAt = record.CreatedAt.Format(time.RFC3339)
value.UpdatedAt = record.UpdatedAt.Format(time.RFC3339)
// delete the templates code end
// Note: if copier.Copy cannot assign a value to a field, add it here
return value, nil
}

View File

@ -6,9 +6,9 @@ import (
"strings"
"time"
"github.com/jinzhu/copier"
"google.golang.org/grpc"
"github.com/go-dev-frame/sponge/pkg/copier"
"github.com/go-dev-frame/sponge/pkg/grpc/interceptor"
"github.com/go-dev-frame/sponge/pkg/logger"
"github.com/go-dev-frame/sponge/pkg/mgo/query"
@ -192,12 +192,8 @@ func convertUserExample(record *model.UserExample) (*serverNameExampleV1.UserExa
if err != nil {
return nil, err
}
// Note: if copier.Copy cannot assign a value to a field, add it here, e.g. CreatedAt, UpdatedAt
// Note: if copier.Copy cannot assign a value to a field, add it here
value.Id = record.ID.Hex()
// todo generate the conversion createdAt and updatedAt code here
// delete the templates code start
value.CreatedAt = record.CreatedAt.Format(time.RFC3339)
value.UpdatedAt = record.UpdatedAt.Format(time.RFC3339)
// delete the templates code end
return value, nil
}

View File

@ -6,9 +6,9 @@ import (
"strings"
"time"
"github.com/jinzhu/copier"
"google.golang.org/grpc"
"github.com/go-dev-frame/sponge/pkg/copier"
"github.com/go-dev-frame/sponge/pkg/grpc/interceptor"
"github.com/go-dev-frame/sponge/pkg/logger"
"github.com/go-dev-frame/sponge/pkg/mgo/query"
@ -317,12 +317,8 @@ func convertUserExample(record *model.UserExample) (*serverNameExampleV1.UserExa
if err != nil {
return nil, err
}
// Note: if copier.Copy cannot assign a value to a field, add it here, e.g. CreatedAt, UpdatedAt
// Note: if copier.Copy cannot assign a value to a field, add it here
value.Id = record.ID.Hex()
// todo generate the conversion createdAt and updatedAt code here
// delete the templates code start
value.CreatedAt = record.CreatedAt.Format(time.RFC3339)
value.UpdatedAt = record.UpdatedAt.Format(time.RFC3339)
// delete the templates code end
return value, nil
}

View File

@ -6,9 +6,9 @@ import (
"strings"
"time"
"github.com/jinzhu/copier"
"google.golang.org/grpc"
"github.com/go-dev-frame/sponge/pkg/copier"
"github.com/go-dev-frame/sponge/pkg/sgorm/query"
"github.com/go-dev-frame/sponge/pkg/grpc/interceptor"
"github.com/go-dev-frame/sponge/pkg/logger"
@ -191,12 +191,7 @@ func convert{{.TableNameCamel}}(record *model.{{.TableNameCamel}}) (*serverNameE
if err != nil {
return nil, err
}
// Note: if copier.Copy cannot assign a value to a field, add it here, e.g. CreatedAt, UpdatedAt
value.{{if .IsStandardPrimaryKey}}Id{{else}}{{.ColumnNameCamel}}{{end}} = record.{{.ColumnNameCamel}}
// todo generate the conversion createdAt and updatedAt code here
// delete the templates code start
value.CreatedAt = record.CreatedAt.Format(time.RFC3339)
value.UpdatedAt = record.UpdatedAt.Format(time.RFC3339)
// delete the templates code end
// Note: if copier.Copy cannot assign a value to a field, add it here
return value, nil
}

View File

@ -5,9 +5,9 @@ import (
"time"
"github.com/DATA-DOG/go-sqlmock"
"github.com/jinzhu/copier"
"github.com/stretchr/testify/assert"
"github.com/go-dev-frame/sponge/pkg/copier"
"github.com/go-dev-frame/sponge/pkg/gotest"
"github.com/go-dev-frame/sponge/pkg/utils"

56
pkg/copier/README.md Normal file
View File

@ -0,0 +1,56 @@
## copyopt
`copier` is `github.com/jinzhu/copier`, default option is add converters for time.Time <--> String.
### Example of use
```go.
package main
import (
"fmt"
"time"
"github.com/go-dev-frame/sponge/pkg/copier"
)
type Model struct {
ID int64
MyIP string
OrderID uint32
CreatedAt *time.Time
UpdatedAt *time.Time
DeletedAt *time.Time
}
type Reply struct {
Id int
MyIp string
OrderId int
CreatedAt string
UpdatedAt string
DeletedAt string
}
func main() {
now := time.Now()
updated := now.Add(time.Hour)
deleted := updated.Add(time.Hour)
src := &Model{
ID: 123,
MyIP: "127.0.0.1",
OrderID: 888,
CreatedAt: &now,
UpdatedAt: &updated,
DeletedAt: &deleted,
}
dst := &Reply{}
err := copier.Copy(dst, src)
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", dst)
}
```

92
pkg/copier/copy.go Normal file
View File

@ -0,0 +1,92 @@
// Package copier is github.com/jinzhu/copier, default option is add converters for time.Time <--> String
package copier
import (
"fmt"
"time"
"github.com/jinzhu/copier"
)
// Copy src to dst with option Converters for time.Time <--> String
func Copy(dst interface{}, src interface{}) error {
return copier.CopyWithOption(dst, src, Converter)
}
// Converter Converters for time.Time <--> String
var Converter = copier.Option{
DeepCopy: true,
Converters: []copier.TypeConverter{
// time.Time to string
{
SrcType: time.Time{},
DstType: copier.String,
Fn: func(src interface{}) (interface{}, error) {
s, ok := src.(time.Time)
if !ok {
return nil, fmt.Errorf("expected time.Time got %T", src)
}
return s.Format(time.RFC3339), nil
},
},
// *time.Time to string
{
SrcType: &time.Time{},
DstType: copier.String,
Fn: func(src interface{}) (interface{}, error) {
s, ok := src.(*time.Time)
if !ok {
return nil, fmt.Errorf("expected *time.Time got %T", src)
}
if s == nil {
return "", nil
}
return s.Format(time.RFC3339), nil
},
},
// string to time.Time
{
SrcType: copier.String,
DstType: time.Time{},
Fn: func(src interface{}) (interface{}, error) {
s, ok := src.(string)
if !ok {
return nil, fmt.Errorf("expected string got %T", src)
}
if s == "" {
return time.Time{}, nil
}
return time.Parse(time.RFC3339, s)
},
},
// string to *time.Time
{
SrcType: copier.String,
DstType: &time.Time{},
Fn: func(src interface{}) (interface{}, error) {
s, ok := src.(string)
if !ok {
return nil, fmt.Errorf("expected string got %T", src)
}
if s == "" {
return nil, nil
}
t, err := time.Parse(time.RFC3339, s)
return &t, err
},
},
},
}
// CopyDefault copy src to dst with default option
func CopyDefault(dst interface{}, src interface{}) error {
return copier.Copy(dst, src)
}
// CopyWithOption copy src to dst with option
func CopyWithOption(dst interface{}, src interface{}, options copier.Option) error {
return copier.CopyWithOption(dst, src, options)
}

478
pkg/copier/copy_test.go Normal file
View File

@ -0,0 +1,478 @@
package copier
import (
"gorm.io/gorm"
"testing"
"time"
"github.com/jinzhu/copier"
"github.com/stretchr/testify/assert"
)
type MyUser1 struct {
Id uint64
MyIp string
OrderId int32
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt time.Time
}
type MyUser2 struct {
ID int64
MyIP string
OrderID uint32
CreatedAt *time.Time
UpdatedAt *time.Time
DeletedAt *time.Time
}
type UserReply struct {
Id int
MyIp string
OrderId int
CreatedAt string
UpdatedAt string
DeletedAt string
}
func copyCustom1() (*UserReply, *UserReply) {
user1 := &MyUser1{
Id: 123,
MyIp: "127.0.0.1",
OrderId: 888,
CreatedAt: time.Now(),
UpdatedAt: time.Now().Add(time.Hour),
DeletedAt: time.Now().Add(-2 * time.Hour),
}
user2 := &MyUser2{
ID: 456,
MyIP: "localhost",
OrderID: 999,
CreatedAt: &user1.CreatedAt,
UpdatedAt: &user1.UpdatedAt,
DeletedAt: &user1.DeletedAt,
}
reply1 := &UserReply{}
reply2 := &UserReply{}
Copy(reply1, user1)
Copy(reply2, user2)
return reply1, reply2
}
func copyStandard1() (*UserReply, *UserReply) {
user1 := &MyUser1{
Id: 789,
MyIp: "127.0.0.1",
OrderId: 888,
CreatedAt: time.Now(),
UpdatedAt: time.Now().Add(time.Hour),
DeletedAt: time.Now().Add(-2 * time.Hour),
}
user2 := &MyUser2{
ID: 1010,
MyIP: "localhost",
OrderID: 999,
CreatedAt: &user1.CreatedAt,
UpdatedAt: &user1.UpdatedAt,
DeletedAt: &user1.DeletedAt,
}
reply1 := &UserReply{}
reply2 := &UserReply{}
copier.Copy(reply1, user1)
copier.Copy(reply2, user2)
return reply1, reply2
}
func copyCustom2() (*MyUser1, *MyUser2) {
req := &UserReply{
Id: 123,
MyIp: "127.0.0.1",
OrderId: 888,
CreatedAt: "2025-05-25T15:38:20+08:00",
UpdatedAt: "2025-05-25T16:38:20+08:00",
DeletedAt: "2025-05-25T17:38:20+08:00",
}
user1 := &MyUser1{}
user2 := &MyUser2{}
Copy(user1, req)
Copy(user2, req)
return user1, user2
}
func copyStandard2() (*MyUser1, *MyUser2) {
req := &UserReply{
Id: 456,
MyIp: "localhost",
OrderId: 888,
CreatedAt: "2025-05-25T15:38:20+08:00",
UpdatedAt: "2025-05-25T16:38:20+08:00",
DeletedAt: "2025-05-25T17:38:20+08:00",
}
user1 := &MyUser1{}
user2 := &MyUser2{}
copier.Copy(user1, req)
copier.Copy(user2, req)
return user1, user2
}
func TestCopyCustom1(t *testing.T) {
reply1, reply2 := copyCustom1()
assert.Equal(t, reply1.Id, 123)
assert.Equal(t, reply2.Id, 456)
t.Log(reply1)
t.Log(reply2)
}
func TestCopyStandard1(t *testing.T) {
reply1, reply2 := copyStandard1()
assert.Equal(t, reply1.Id, 789)
assert.Equal(t, reply2.Id, 1010)
t.Log(reply1)
t.Log(reply2)
}
func TestCopyCustom2(t *testing.T) {
user1, user2 := copyCustom2()
assert.Equal(t, int(user1.Id), 123)
assert.Equal(t, int(user2.ID), 123)
t.Log(user1)
t.Log(user2)
}
func TestCopyStandard2(t *testing.T) {
user1, user2 := copyStandard2()
assert.Equal(t, int(user1.Id), 456)
assert.Equal(t, int(user2.ID), 456)
t.Log(user1)
t.Log(user2)
}
func BenchmarkCopyStandard1(b *testing.B) {
for i := 0; i < b.N; i++ {
copyStandard1()
}
}
func BenchmarkCopyCustom1(b *testing.B) {
for i := 0; i < b.N; i++ {
copyCustom1()
}
}
func BenchmarkCopyStandard2(b *testing.B) {
for i := 0; i < b.N; i++ {
copyStandard2()
}
}
func BenchmarkCopyCustom2(b *testing.B) {
for i := 0; i < b.N; i++ {
copyCustom2()
}
}
// -------------------------------------------------
func SliceData1() []*MyUser1 {
users := []*MyUser1{
{
Id: 123,
MyIp: "127.0.0.1",
OrderId: 888,
CreatedAt: time.Now(),
UpdatedAt: time.Now().Add(time.Hour),
DeletedAt: time.Now().Add(-2 * time.Hour),
},
{
Id: 456,
MyIp: "localhost",
OrderId: 999,
CreatedAt: time.Now().Add(2 * time.Hour),
UpdatedAt: time.Now().Add(3 * time.Hour),
DeletedAt: time.Now().Add(-3 * time.Hour),
},
{
Id: 789,
MyIp: "192.168.1.1",
OrderId: 1010,
CreatedAt: time.Now().Add(-2 * time.Hour),
UpdatedAt: time.Now().Add(-3 * time.Hour),
DeletedAt: time.Now().Add(3 * time.Hour),
},
}
return users
}
func SliceData2() []*MyUser2 {
now := time.Now()
updated := now.Add(time.Hour)
deleted := now.Add(-2 * time.Hour)
users := []*MyUser2{
{
ID: 123,
MyIP: "127.0.0.1",
OrderID: 888,
CreatedAt: &now,
UpdatedAt: &updated,
DeletedAt: &deleted,
},
{
ID: 456,
MyIP: "localhost",
OrderID: 999,
CreatedAt: &now,
UpdatedAt: &updated,
DeletedAt: &deleted,
},
{
ID: 789,
MyIP: "192.168.1.1",
OrderID: 1010,
CreatedAt: &now,
UpdatedAt: &updated,
DeletedAt: &deleted,
},
}
return users
}
func SliceData3() []*UserReply {
userReplies := []*UserReply{
{
Id: 123,
MyIp: "127.0.0.1",
OrderId: 888,
CreatedAt: "2025-05-25T15:38:20+08:00",
UpdatedAt: "2025-05-25T16:38:20+08:00",
DeletedAt: "2025-05-25T17:38:20+08:00",
},
{
Id: 456,
MyIp: "localhost",
OrderId: 999,
CreatedAt: "2025-05-25T15:38:20+08:00",
UpdatedAt: "2025-05-25T16:38:20+08:00",
DeletedAt: "2025-05-25T17:38:20+08:00",
},
{
Id: 789,
MyIp: "192.168.1.1",
OrderId: 1010,
CreatedAt: "2025-05-25T15:38:20+08:00",
UpdatedAt: "2025-05-25T16:38:20+08:00",
DeletedAt: "2025-05-25T17:38:20+08:00",
},
}
return userReplies
}
func TestSliceCopyCustom1(t *testing.T) {
users := SliceData1()
replies := make([]*UserReply, len(users))
Copy(&replies, &users)
for _, reply := range replies {
t.Log(reply)
}
}
func TestSliceCopyStandard1(t *testing.T) {
users := SliceData1()
replies := make([]*UserReply, len(users))
copier.Copy(&replies, &users)
for _, reply := range replies {
t.Log(reply)
}
}
func TestSliceCopyCustom2(t *testing.T) {
users := SliceData2()
replies := make([]*UserReply, len(users))
Copy(&replies, &users)
for _, reply := range replies {
t.Log(reply)
}
}
func TestSliceCopyStandard2(t *testing.T) {
users := SliceData2()
replies := make([]*UserReply, len(users))
copier.Copy(&replies, &users)
for _, reply := range replies {
t.Log(reply)
}
}
func TestSliceCopyCustom3(t *testing.T) {
users := SliceData3()
replies := make([]*MyUser1, len(users))
Copy(&replies, &users)
for _, reply := range replies {
t.Log(reply)
}
}
func TestSliceCopyStandard3(t *testing.T) {
users := SliceData3()
replies := make([]*MyUser1, len(users))
copier.Copy(&replies, &users)
for _, reply := range replies {
t.Log(reply)
}
}
func TestSliceCopyCustom4(t *testing.T) {
users := SliceData3()
replies := make([]*MyUser2, len(users))
Copy(&replies, &users)
for _, reply := range replies {
t.Log(reply)
}
}
func TestSliceCopyStandard4(t *testing.T) {
users := SliceData3()
replies := make([]*MyUser2, len(users))
copier.Copy(&replies, &users)
for _, reply := range replies {
t.Log(reply)
}
}
func BenchmarkSliceCopyStandard1(b *testing.B) {
users := SliceData1()
for i := 0; i < b.N; i++ {
replies := make([]*UserReply, len(users))
copier.Copy(&replies, &users)
}
}
func BenchmarkSliceCopyStandardSingle1(b *testing.B) {
users := SliceData1()
for i := 0; i < b.N; i++ {
reply := &UserReply{}
for _, user := range users {
copier.Copy(reply, user)
}
}
}
func BenchmarkSliceCopyCustom1(b *testing.B) {
users := SliceData1()
for i := 0; i < b.N; i++ {
replies := make([]*UserReply, len(users))
Copy(&replies, &users)
}
}
func BenchmarkSliceCopyCustomSingle1(b *testing.B) {
users := SliceData1()
for i := 0; i < b.N; i++ {
reply := &UserReply{}
for _, user := range users {
Copy(reply, user)
}
}
}
// ---------------------------------------------------------------------
func TestCopyDefault(t *testing.T) {
user1 := &MyUser1{
Id: 123,
MyIp: "127.0.0.1",
OrderId: 888,
CreatedAt: time.Now(),
UpdatedAt: time.Now().Add(time.Hour),
DeletedAt: time.Now().Add(2 * time.Hour),
}
user2 := &MyUser2{}
err := CopyDefault(user2, user1)
if err != nil {
t.Error(err)
return
}
assert.Equal(t, user2.ID, int64(123))
t.Log(user2)
}
func TestCopyWithOption(t *testing.T) {
user1 := &MyUser1{
Id: 123,
MyIp: "127.0.0.1",
OrderId: 888,
CreatedAt: time.Now(),
UpdatedAt: time.Now().Add(time.Hour),
DeletedAt: time.Now().Add(2 * time.Hour),
}
user2 := &MyUser2{}
option := copier.Option{
DeepCopy: true,
}
err := CopyWithOption(user2, user1, option)
if err != nil {
t.Error(err)
return
}
assert.Equal(t, user2.ID, int64(123))
t.Log(user2)
}
func TestCopyEmbedStruct(t *testing.T) {
type Model struct {
ID uint64 `gorm:"column:id;AUTO_INCREMENT;primary_key" json:"id"`
CreatedAt time.Time `gorm:"column:created_at" json:"createdAt"`
UpdatedAt time.Time `gorm:"column:updated_at" json:"updatedAt"`
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;index" json:"-"`
}
type User struct {
Model `gorm:"embedded"`
Name string `gorm:"column:name" json:"name"`
Age int `gorm:"column:age" json:"age"`
}
user := &User{
Model: Model{
ID: 123,
CreatedAt: time.Now(),
UpdatedAt: time.Now().Add(time.Hour),
DeletedAt: gorm.DeletedAt{Time: time.Now().Add(2 * time.Hour)},
},
Name: "test",
Age: 18,
}
type UserReply struct {
Id uint64 `json:"id"`
Age int `json:"age"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
DeletedAt string `json:"deletedAt"`
}
reply := &UserReply{}
err := Copy(reply, user)
if err != nil {
t.Error(err)
return
}
reply.DeletedAt = user.DeletedAt.Time.Format(time.RFC3339)
t.Log(*reply)
}