Skip to content

Commit

Permalink
Add xgo pb
Browse files Browse the repository at this point in the history
  • Loading branch information
glassonion1 committed Aug 18, 2024
1 parent de97d1a commit 51880cc
Show file tree
Hide file tree
Showing 6 changed files with 491 additions and 0 deletions.
63 changes: 63 additions & 0 deletions xgopb/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# xgo pb - protobuf extension
The xgo pb is a protobuf extension of xgo.

## Features
- Deep copy

## Install
```
$ go get github.com/glassonion1/xgo/xgopb
```

## Import
```go
import "github.com/glassonion1/xgo/xgopb"
```

## Usage
### Deep copy

```go
package xgopb_test

import (
"fmt"
"time"

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

// 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
}

func Example() {
now := time.Date(2025, 6, 1, 0, 0, 0, 0, time.UTC)
from := FromModel{
ID: "xxxx",
Name: "R2D2",
CreatedAt: now,
UpdatedAt: &now,
}
to := &ToModel{}
err := xgo.DeepCopy(from, to)
if err != nil {
// handles error
}
fmt.Println("ToModel object:", to)

// Output: ToModel object: &{xxxx R2D2 seconds:1748736000 seconds:1748736000}
}
```
120 changes: 120 additions & 0 deletions xgopb/copier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package xgopb

import (
"reflect"
"time"

"github.com/glassonion1/xgo"
"google.golang.org/protobuf/types/known/durationpb"
"google.golang.org/protobuf/types/known/timestamppb"
)

// DeepCopy
func DeepCopy(srcModel interface{}, dstModel interface{}) error {
return xgo.DeepCopyWithCustomSetter(srcModel, dstModel, setTimeField)
}

func setTimeField(src, dst reflect.Value) (bool, error) {

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

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

case *timestamppb.Timestamp:
if t == nil {
return false, nil
}
if t.GetSeconds() <= 0 {
return false, nil
}
if err := t.CheckValid(); err != nil {
return false, err
}
// *timestamppb.Timestamp -> time.Time
switch dst.Interface().(type) {
case time.Time:
dst.Set(reflect.ValueOf(t.AsTime()))
return true, nil
case *time.Time:
dst.Set(reflect.ValueOf(xgo.ToPtr(t.AsTime())))
return true, nil
}

case time.Duration:
if t.Nanoseconds() <= 0 {
return false, nil
}
switch dst.Interface().(type) {
case *durationpb.Duration:
d := durationpb.New(t)
if err := d.CheckValid(); err != nil {
return false, err
}
dst.Set(reflect.ValueOf(d))
return true, nil
}

case *durationpb.Duration:
if t == nil {
return false, nil
}
if err := t.CheckValid(); err != nil {
return false, err
}
// *durationpb.Duration -> time.Duration
switch dst.Interface().(type) {
case time.Duration:
dst.Set(reflect.ValueOf(t.AsDuration()))
return true, nil
}

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

0 comments on commit 51880cc

Please sign in to comment.