Skip to content

Commit

Permalink
Separated protobuf related functions
Browse files Browse the repository at this point in the history
  • Loading branch information
glassonion1 committed Aug 18, 2024
1 parent 86cb659 commit de97d1a
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 389 deletions.
42 changes: 20 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,30 +45,27 @@ import (
"fmt"
"time"

"github.com/golang/protobuf/ptypes/timestamp"
"github.com/glassonion1/xgo"
)

// It is a common, ordinary struct
type FromModel struct {
ID string `copier:"Id"`
Name string
CreatedAt time.Time
UpdatedAt *time.Time
}
// It is like a protobuf struct on gRPC
type ToModel struct {
Id string
Name string
CreatedAt *timestamp.Timestamp
UpdatedAt *timestamp.Timestamp
CreatedAt time.Time
UpdatedAt *time.Time
}

func Example() {
now := time.Date(2020, 6, 1, 0, 0, 0, 0, time.UTC)
now := time.Date(2025, 6, 1, 0, 0, 0, 0, time.UTC)
from := FromModel{
ID: "xxxx",
Name: "R2D2",
ID: "xxxx",
Name: "R2D2",
CreatedAt: now,
UpdatedAt: &now,
}
Expand All @@ -78,8 +75,8 @@ func Example() {
// handles error
}
fmt.Println("ToModel object:", to)
// Output: ToModel object: &{xxxx R2D2 seconds:1590969600 seconds:1590969600}

// Output: ToModel object: &{xxxx R2D2 2025-06-01 00:00:00 +0000 UTC 2025-06-01 00:00:00 +0000 UTC}
}
```

Expand All @@ -100,7 +97,7 @@ func Example() {
ID: "xxxx1",
Name: "R2D2",
},
{
{
ID: "xxxx2",
Name: "C3PO",
},
Expand All @@ -120,19 +117,19 @@ func Example() {
Contains method for a slice.
```go
// slice of int32
containsInt32 := xgo.Contains[int32]([]int32{1, 2, 3, 4, 5}, 3)
containsInt32 := xgo.Contains([]int32{1, 2, 3, 4, 5}, 3)
fmt.Println("contains int32:", containsInt32)

// slice of int
containsInt := xgo.Contains[int]([]int{1, 2, 3, 4, 5}, 2)
containsInt := xgo.Contains([]int{1, 2, 3, 4, 5}, 2)
fmt.Println("contains int:", containsInt)

// slice of float64
containsFloat64 := xgo.Contains[float64]([]float64{1.1, 2.2, 3.3, 4.4, 5.5}, 4.4)
containsFloat64 := xgo.Contains([]float64{1.1, 2.2, 3.3, 4.4, 5.5}, 4.4)
fmt.Println("contains float64:", containsFloat64)

// slice of string
containsString := Contains[string]([]string{"r2d2", "c3po", "bb8"}, "c3po")
containsString := xgo.Contains([]string{"r2d2", "c3po", "bb8"}, "c3po")
fmt.Println(containsString) // -> true

// slice of struct
Expand All @@ -158,17 +155,18 @@ target := hero{
ID: "2",
Name: "Han Solo",
}
containsStruct := xgo.Contains[hero](list, target)
containsStruct := xgo.Contains(list, target)
fmt.Println("contains struct:", containsStruct)

// Output:
// contains int32: true
// contains int: true
// contains float64: true
// contains string: true
// contains struct: true
```

### New
### ToPtr
Obtain pointers to types
```go
type Vegetables string
Expand All @@ -186,12 +184,12 @@ type Model struct {
CreatedAt *time.Time
}

func ExampleNew() {
func ExampleToPtr() {
obj := Model{
ID: xgo.New(123),
Name: xgo.New("R2D2"),
Material: xgo.New(Pea),
CreatedAt: xgo.New(time.Date(2020, 6, 1, 0, 0, 0, 0, time.UTC)),
ID: xgo.ToPtr(123),
Name: xgo.ToPtr("R2D2"),
Material: xgo.ToPtr(Pea),
CreatedAt: xgo.ToPtr(time.Date(2020, 6, 1, 0, 0, 0, 0, time.UTC)),
}
fmt.Println("object:", obj)
}
Expand Down
126 changes: 35 additions & 91 deletions copier.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,26 @@ import (
"fmt"
"reflect"
"time"

"google.golang.org/protobuf/types/known/durationpb"
"google.golang.org/protobuf/types/known/timestamppb"
)

// tagCopier is tag for deep copy target
const tagCopier = "copier"

// DeepCopy deepcopy a struct to struct.
type SetCustomField func(src, dst reflect.Value) (bool, error)

// DeepCopy
func DeepCopy(srcModel interface{}, dstModel interface{}) error {
f := func(src, dst reflect.Value) (bool, error) { return false, nil }
return DeepCopyWithCustomSetter(srcModel, dstModel, f)
}

// DeepCopy with custom setter
func DeepCopyWithCustomSetter(
srcModel interface{},
dstModel interface{},
customSetter SetCustomField,
) error {

src := reflect.Indirect(reflect.ValueOf(srcModel))
dst := reflect.Indirect(reflect.ValueOf(dstModel))

Expand Down Expand Up @@ -65,24 +75,31 @@ func DeepCopy(srcModel interface{}, dstModel interface{}) error {
continue
}

// string, int, float
if srcFieldType.Type.ConvertibleTo(dstFieldType.Type) {
dstFieldValue.Set(srcFieldValue.Convert(dstFieldType.Type))
continue
}

isSet, err := setTimeField(srcFieldValue, dstFieldValue)
isSet, err := customSetter(srcFieldValue, dstFieldValue)
if err != nil {
return fmt.Errorf("%v", err)
}
if isSet {
continue
}

// from struct to pointer
switch srcFieldValue.Kind() {
case reflect.Int64, reflect.Int32:
dstFieldValue.SetInt(srcFieldValue.Int())
// set the time.Time field
isSet, err = setTimeField(srcFieldValue, dstFieldValue)
if err != nil {
return fmt.Errorf("%v", err)
}
if isSet {
continue
}

// struct, pointer, slice
switch srcFieldValue.Kind() {
case reflect.Struct:
if !field.Anonymous {
dv, vFunc := instantiate(dstFieldValue)
Expand All @@ -109,13 +126,13 @@ func DeepCopy(srcModel interface{}, dstModel interface{}) error {
}
dstFieldValue.Set(vFunc())
continue

case reflect.Slice:
if err := copySlice(srcFieldValue, dstFieldValue); err != nil {
return fmt.Errorf("%v", err)
}
continue
}

}

return nil
Expand Down Expand Up @@ -160,108 +177,35 @@ func setTimeField(src, dst reflect.Value) (bool, error) {
case time.Time:
// time.Time -> *timestampps.Timestamp or int64
switch dst.Interface().(type) {
case *timestamppb.Timestamp:
if t.Unix() > 0 {
ts := timestamppb.New(t)
if err := ts.CheckValid(); err != nil {
return false, err
}
dst.Set(reflect.ValueOf(ts))
}
case int64:
dst.Set(reflect.ValueOf(t.Unix()))
return true, nil
case *time.Time:
dst.Set(reflect.ValueOf(&t))
return true, nil
}
return true, nil

case *time.Time:
if src.IsNil() {
dst.Set(reflect.Zero(dst.Type()))
return true, nil
if t == nil {
return false, nil
}
// *time.Time -> timestampps.Timestamp or int64
switch dst.Interface().(type) {
case *timestamppb.Timestamp:
if t.Unix() > 0 {
ts := timestamppb.New(*t)
if err := ts.CheckValid(); err != nil {
return false, err
}
dst.Set(reflect.ValueOf(ts))
}
case int64:
dst.Set(reflect.ValueOf(t.Unix()))
return true, nil
case time.Time:
dst.Set(reflect.ValueOf(*t))
return true, nil
}
return true, nil
case *timestamppb.Timestamp:
// *timestamppb.Timestamp -> time.Time
switch dst.Interface().(type) {
case time.Time:
if t.GetSeconds() > 0 {
ts := timestamppb.New(t.AsTime())
if err := ts.CheckValid(); err != nil {
return false, err
}
dst.Set(reflect.ValueOf(ts.AsTime()))
}
case *time.Time:
if t.GetSeconds() > 0 {
ts := timestamppb.New(t.AsTime())
if err := ts.CheckValid(); err != nil {
return false, err
}
time := ts.AsTime()
dst.Set(reflect.ValueOf(&time))
}
}
return true, nil

case time.Duration:
// time.Duration -> *durationpb.Duration
switch dst.Interface().(type) {
case *durationpb.Duration:
if t.Nanoseconds() > 0 {
dst.Set(reflect.ValueOf(durationpb.New(t)))
}
}
return true,
nil
case *durationpb.Duration:
// *durationpb.Duration -> time.Duration
switch dst.Interface().(type) {
case time.Duration:
if t == nil {
return false, nil
}

d := durationpb.New(t.AsDuration())
if err := d.CheckValid(); err != nil {
return false, err
}
dst.Set(reflect.ValueOf(d.AsDuration()))
}
return true, nil
case int64:
// int64 -> time.Time or *timestamppb.Timestamp or *durationpb.Duration
switch dst.Interface().(type) {
case time.Time:
dst.Set(reflect.ValueOf(time.Unix(t, 0)))
case *timestamppb.Timestamp:
if t > 0 {
ts := timestamppb.New(time.Unix(t, 0))
if err := ts.CheckValid(); err != nil {
return false, err
}
dst.Set(reflect.ValueOf(ts))
}
case *durationpb.Duration:
if t > 0 {
dst.Set(reflect.ValueOf(durationpb.New(time.Duration(t))))
}
return true, nil
}
return true, nil
}
return false, nil
}
Loading

0 comments on commit de97d1a

Please sign in to comment.