Skip to content

Commit

Permalink
moved setMarker to separate project
Browse files Browse the repository at this point in the history
  • Loading branch information
adranwit committed Jun 4, 2023
1 parent a83f911 commit 1eba909
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 104 deletions.
66 changes: 58 additions & 8 deletions differ_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ import (

func TestNewDiffer(t *testing.T) {

type XEntry struct {
ID int
Name string
}

type Holder struct {
ID int
Entries []*XEntry `diff:"indexBy=ID"`
}

type PresRecordHas struct {
ID bool
Name bool
Expand All @@ -23,7 +33,7 @@ func TestNewDiffer(t *testing.T) {
ScratchPad string `diff:"-"`
Time time.Time
Bar int
Has *PresRecordHas `diff:"presence=true"`
Has *PresRecordHas `setMarker:"true"`
}

type Record struct {
Expand Down Expand Up @@ -225,6 +235,16 @@ func TestNewDiffer(t *testing.T) {
{Type: "create", Path: &Path{Kind: PathKindIndex, Path: &Path{Kind: PathKindKey, Path: &Path{Kind: PathKinField, Path: &Path{}, Name: "ExprList"}, Key: "k1"}, Index: 0}, To: "0"},
}},
},

{
description: "repeated - update - shallow",
from: &Repeated{ID: 1, Records: []*Record{{ID: 12}}, Nums: []int{10, 2}},
to: &Repeated{ID: 2, Records: []*Record{{ID: 23}}},
options: []Option{WithShallow(true)},
expect: &ChangeLog{Changes: []*Change{
{Type: "update", Path: &Path{Kind: 1, Path: &Path{}, Name: "ID"}, From: 1, To: 2},
}},
},
{description: "diff with field presence check",
from: &PresRecord{ID: 20, Name: "abc", Bar: 23,
Has: &PresRecordHas{
Expand All @@ -240,21 +260,51 @@ func TestNewDiffer(t *testing.T) {

options: []Option{WithPresence(true)},
expect: &ChangeLog{Changes: []*Change{
{Type: "update", Path: &Path{Kind: PathKinField, Path: &Path{}, Name: "ID"}, From: 20, To: 21},
{Type: "update", Path: &Path{Kind: PathKinField, Path: &Path{}, Name: "Name"}, From: "abc", To: "xyz"},
}},
},

{
description: "repeated - update - shallow",
from: &Repeated{ID: 1, Records: []*Record{{ID: 12}}, Nums: []int{10, 2}},
to: &Repeated{ID: 2, Records: []*Record{{ID: 23}}},
options: []Option{WithShallow(true)},
description: "index by filed",
from: &Holder{
ID: 1,
Entries: []*XEntry{
{
ID: 1,
Name: "Name 1",
},
{
ID: 2,
Name: "Name 2",
},
},
},
to: &Holder{
ID: 1,
Entries: []*XEntry{
{
ID: 2,
Name: "Name 2",
},
{
ID: 1,
Name: "Name 1",
},
{
ID: 3,
Name: "Name 3",
},
},
},
expect: &ChangeLog{Changes: []*Change{
{Type: "update", Path: &Path{Kind: 1, Path: &Path{}, Name: "ID"}, From: 1, To: 2},

{Type: "create", Path: &Path{Kind: PathKindIndex, Index: 2, Path: &Path{Kind: PathKinField, Path: &Path{}, Name: "Entries"}},
From: (interface{})(nil),
To: &XEntry{ID: 3, Name: "Name 3"}},
}},
},
}
for _, testCase := range testCases {
for _, testCase := range testCases[len(testCases)-1:] {
differ, err := New(reflect.TypeOf(testCase.from), reflect.TypeOf(testCase.to), testCase.configOptions...)
if !assert.Nil(t, err, testCase.description) {
continue
Expand Down
3 changes: 3 additions & 0 deletions field.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package godiff

import (
"github.com/viant/structology"
"github.com/viant/xunsafe"
"reflect"
"strings"
Expand Down Expand Up @@ -50,6 +51,8 @@ func (m *matcher) build(xStruct *xunsafe.Struct, config *Config) {
xField := &xStruct.Fields[i]
tag, _ := ParseTag(string(xField.Tag))
tag.init(config)

tag.PresenceMarker = structology.IsSetMarker(xField.Tag)
fieldAccessor := newAccessor(i, xField, tag)
m.index[xField.Name] = &fieldAccessor
m.index[strings.ToLower(xField.Name)] = &fieldAccessor
Expand Down
10 changes: 6 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ module github.com/viant/godiff
go 1.17

require (
github.com/stretchr/testify v1.7.0
github.com/stretchr/testify v1.8.4
github.com/viant/parsly v0.1.0
github.com/viant/xunsafe v0.8.1
github.com/viant/structology v0.1.0
github.com/viant/xunsafe v0.8.4
)

require (
github.com/davecgh/go-spew v1.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
github.com/viant/xreflect v0.0.0-20230303201326-f50afb0feb0d // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
28 changes: 26 additions & 2 deletions index.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package godiff

import (
"fmt"
"github.com/viant/xunsafe"
"reflect"
"unsafe"
)

Expand All @@ -10,13 +12,35 @@ type entry struct {
value interface{}
}

type indexer struct{}
type indexer struct {
field *xunsafe.Field
}

func (i *indexer) indexBy(xSlice *xunsafe.Slice, ptr unsafe.Pointer, by string) map[interface{}]*entry {
if by == "" || by == "." {
return i.indexPrimitive(xSlice, ptr)
}
panic("not yet supported")
elemType := xSlice.Type.Elem()
if elemType.Kind() == reflect.Ptr {
elemType = elemType.Elem()
}
if elemType.Kind() != reflect.Struct {
panic(fmt.Sprintf("%s not yet supported", elemType.String()))
}
i.field = xunsafe.FieldByName(elemType, by)
return i.indexByField(xSlice, ptr)
}

func (i *indexer) indexByField(xSlice *xunsafe.Slice, ptr unsafe.Pointer) map[interface{}]*entry {
var result = make(map[interface{}]*entry)
l := xSlice.Len(ptr)
for j := 0; j < l; j++ {
value := xSlice.ValueAt(ptr, j)
ptr := xunsafe.AsPointer(value)
key := i.field.Value(ptr)
result[key] = &entry{index: j, value: value}
}
return result
}

func (i *indexer) indexPrimitive(xSlice *xunsafe.Slice, ptr unsafe.Pointer) map[interface{}]*entry {
Expand Down
57 changes: 0 additions & 57 deletions presence.go

This file was deleted.

46 changes: 21 additions & 25 deletions struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,20 @@ package godiff

import (
"fmt"
"github.com/viant/structology"
"github.com/viant/xunsafe"
"reflect"
)

type (
structDiffer struct {
config *Config
from *xunsafe.Struct
to *xunsafe.Struct
fromType reflect.Type
toType reflect.Type
fields []*field
presenceProvider PresenceProvider
config *Config
from *xunsafe.Struct
to *xunsafe.Struct
fromType reflect.Type
toType reflect.Type
fields []*field
marker structology.Marker
}
)

Expand All @@ -35,15 +36,13 @@ func (s *structDiffer) diff(changeLog *ChangeLog, path *Path, from, to interface
}
if options.presence {
hasPtr := toPtr
if s.presenceProvider.Holder != nil {
if s.presenceProvider.Holder.IsNil(toPtr) {
hasPtr = fromPtr
}
if s.marker.CanUseHolder(toPtr) {
hasPtr = fromPtr
}
if !s.presenceProvider.IsFieldSet(hasPtr, int(field.to.Index)) {
if !s.marker.IsSet(hasPtr, int(field.to.Index)) {
continue //skip diff, to/dest is not set
}
if field.tag != nil && field.tag.Presence {
if field.tag != nil && field.tag.PresenceMarker {
continue //do not compare presence tag
}
}
Expand All @@ -65,7 +64,7 @@ func (s *structDiffer) diff(changeLog *ChangeLog, path *Path, from, to interface
}
}
if fromValue == nil {

diffChangeType = ChangeTypeCreate
}
if err = field.differ.diff(changeLog, path.Field(field.name), fromValue, toValue, diffChangeType, options); err != nil {
return err
Expand Down Expand Up @@ -100,18 +99,17 @@ func (s *structDiffer) matchFields() error {
matcher.build(s.to, s.config)
}

var filedPos = map[string]int{}
marker, err := structology.NewMarker(s.fromType)
if err == nil {
s.marker = *marker
}
for i := range s.from.Fields {
fromField := &s.from.Fields[i]
filedPos[fromField.Name] = int(fromField.Index)
tag, err := ParseTag(fromField.Tag.Get(s.config.TagName))
if err != nil {
return err
}
if tag.Presence {
s.presenceProvider.Holder = fromField
continue
}

if tag.Ignore {
continue
}
Expand All @@ -137,6 +135,7 @@ func (s *structDiffer) matchFields() error {
}
aField.differ = &Differ{config: s.config, mapDiffer: differ}
case reflect.Struct:

if aField.from.Type == s.fromType {
aField.differ = &Differ{config: s.config, structDiffer: s}
continue
Expand Down Expand Up @@ -165,15 +164,12 @@ func (s *structDiffer) matchFields() error {
}
}
}
if s.presenceProvider.Holder != nil {
s.presenceProvider.Init(filedPos)
}
s.fields = fields
return nil
}

func newStructDiffer(from, to reflect.Type, config *Config) (*structDiffer, error) {
var result = structDiffer{config: config, fromType: from, toType: timeType}
var result = structDiffer{config: config, fromType: from, toType: to}

fromType := structType(from)
if fromType == nil {
Expand All @@ -183,6 +179,7 @@ func newStructDiffer(from, to reflect.Type, config *Config) (*structDiffer, erro
if toType == nil {
return nil, fmt.Errorf("invalid 'to' struct type: %s", to.String())
}

result.from = xunsafe.NewStruct(fromType)
result.to = result.from
if toType != fromType {
Expand All @@ -191,6 +188,5 @@ func newStructDiffer(from, to reflect.Type, config *Config) (*structDiffer, erro
if err := result.matchFields(); err != nil {
return nil, err
}

return &result, nil
}
14 changes: 6 additions & 8 deletions tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import (

//Tag represents a tag
type Tag struct {
Name string
Presence bool
PairSeparator string
PairDelimiter string
pairDelimiter []string
ItemSeparator string
Name string
PresenceMarker bool
PairSeparator string
PairDelimiter string
pairDelimiter []string
ItemSeparator string

Whitespace string
IndexBy string
Expand Down Expand Up @@ -76,8 +76,6 @@ func ParseTag(tagString string) (*Tag, error) {
switch len(nv) {
case 2:
switch strings.ToLower(strings.TrimSpace(nv[0])) {
case "presence":
tag.Presence = true
case "ignore":
tag.Ignore = true
case "name":
Expand Down

0 comments on commit 1eba909

Please sign in to comment.