Skip to content

Commit 0f0841e

Browse files
authored
Added DiffType and DiffFunc to ValueDiffer interface (#83)
Added the ability to intercept the data using custom differs
1 parent 22500a7 commit 0f0841e

File tree

5 files changed

+141
-31
lines changed

5 files changed

+141
-31
lines changed

diff.go

Lines changed: 87 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,56 @@ const (
2323
DELETE = "delete"
2424
)
2525

26+
// DiffType represents an enum with all the supported diff types
27+
type DiffType uint8
28+
29+
const (
30+
UNSUPPORTED DiffType = iota
31+
STRUCT
32+
SLICE
33+
ARRAY
34+
STRING
35+
BOOL
36+
INT
37+
UINT
38+
FLOAT
39+
MAP
40+
PTR
41+
INTERFACE
42+
)
43+
44+
func (t DiffType) String() string {
45+
switch t {
46+
case STRUCT:
47+
return "STRUCT"
48+
case SLICE:
49+
return "SLICE"
50+
case ARRAY:
51+
return "ARRAY"
52+
case STRING:
53+
return "STRING"
54+
case BOOL:
55+
return "BOOL"
56+
case INT:
57+
return "INT"
58+
case UINT:
59+
return "UINT"
60+
case FLOAT:
61+
return "FLOAT"
62+
case MAP:
63+
return "MAP"
64+
case PTR:
65+
return "PTR"
66+
case INTERFACE:
67+
return "INTERFACE"
68+
default:
69+
return "UNSUPPORTED"
70+
}
71+
}
72+
73+
// DiffFunc represents the built-in diff functions
74+
type DiffFunc func([]string, reflect.Value, reflect.Value, interface{}) error
75+
2676
// Differ a configurable diff instance
2777
type Differ struct {
2878
TagName string
@@ -53,7 +103,7 @@ type Change struct {
53103
// ValueDiffer is an interface for custom differs
54104
type ValueDiffer interface {
55105
Match(a, b reflect.Value) bool
56-
Diff(cl *Changelog, path []string, a, b reflect.Value) error
106+
Diff(dt DiffType, df DiffFunc, cl *Changelog, path []string, a, b reflect.Value, parent interface{}) error
57107
InsertParentDiffer(dfunc func(path []string, a, b reflect.Value, p interface{}) error)
58108
}
59109

@@ -134,6 +184,35 @@ func (cl *Changelog) Filter(path []string) Changelog {
134184
return ncl
135185
}
136186

187+
func (d *Differ) getDiffType(a, b reflect.Value) (DiffType, DiffFunc) {
188+
switch {
189+
case are(a, b, reflect.Struct, reflect.Invalid):
190+
return STRUCT, d.diffStruct
191+
case are(a, b, reflect.Slice, reflect.Invalid):
192+
return SLICE, d.diffSlice
193+
case are(a, b, reflect.Array, reflect.Invalid):
194+
return ARRAY, d.diffSlice
195+
case are(a, b, reflect.String, reflect.Invalid):
196+
return STRING, d.diffString
197+
case are(a, b, reflect.Bool, reflect.Invalid):
198+
return BOOL, d.diffBool
199+
case are(a, b, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Invalid):
200+
return INT, d.diffInt
201+
case are(a, b, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Invalid):
202+
return UINT, d.diffUint
203+
case are(a, b, reflect.Float32, reflect.Float64, reflect.Invalid):
204+
return FLOAT, d.diffFloat
205+
case are(a, b, reflect.Map, reflect.Invalid):
206+
return MAP, d.diffMap
207+
case are(a, b, reflect.Ptr, reflect.Invalid):
208+
return PTR, d.diffPtr
209+
case are(a, b, reflect.Interface, reflect.Invalid):
210+
return INTERFACE, d.diffInterface
211+
default:
212+
return UNSUPPORTED, nil
213+
}
214+
}
215+
137216
// Diff returns a changelog of all mutated values from both
138217
func (d *Differ) Diff(a, b interface{}) (Changelog, error) {
139218
// reset the state of the diff
@@ -160,11 +239,14 @@ func (d *Differ) diff(path []string, a, b reflect.Value, parent interface{}) err
160239
return ErrTypeMismatch
161240
}
162241

242+
// get the diff type and the corresponding built-int diff function to handle this type
243+
diffType, diffFunc := d.getDiffType(a, b)
244+
163245
// first go through custom diff functions
164246
if len(d.customValueDiffers) > 0 {
165247
for _, vd := range d.customValueDiffers {
166248
if vd.Match(a, b) {
167-
err := vd.Diff(&d.cl, path, a, b)
249+
err := vd.Diff(diffType, diffFunc, &d.cl, path, a, b, parent)
168250
if err != nil {
169251
return err
170252
}
@@ -174,32 +256,11 @@ func (d *Differ) diff(path []string, a, b reflect.Value, parent interface{}) err
174256
}
175257

176258
// then built-in diff functions
177-
switch {
178-
case are(a, b, reflect.Struct, reflect.Invalid):
179-
return d.diffStruct(path, a, b)
180-
case are(a, b, reflect.Slice, reflect.Invalid):
181-
return d.diffSlice(path, a, b)
182-
case are(a, b, reflect.Array, reflect.Invalid):
183-
return d.diffSlice(path, a, b)
184-
case are(a, b, reflect.String, reflect.Invalid):
185-
return d.diffString(path, a, b, parent)
186-
case are(a, b, reflect.Bool, reflect.Invalid):
187-
return d.diffBool(path, a, b, parent)
188-
case are(a, b, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Invalid):
189-
return d.diffInt(path, a, b, parent)
190-
case are(a, b, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Invalid):
191-
return d.diffUint(path, a, b, parent)
192-
case are(a, b, reflect.Float32, reflect.Float64, reflect.Invalid):
193-
return d.diffFloat(path, a, b, parent)
194-
case are(a, b, reflect.Map, reflect.Invalid):
195-
return d.diffMap(path, a, b)
196-
case are(a, b, reflect.Ptr, reflect.Invalid):
197-
return d.diffPtr(path, a, b, parent)
198-
case are(a, b, reflect.Interface, reflect.Invalid):
199-
return d.diffInterface(path, a, b, parent)
200-
default:
259+
if diffType == UNSUPPORTED {
201260
return errors.New("unsupported type: " + a.Kind().String())
202261
}
262+
263+
return diffFunc(path, a, b, parent)
203264
}
204265

205266
func (cl *Changelog) Add(t string, path []string, ftco ...interface{}) {

diff_map.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
"github.com/vmihailenco/msgpack"
1212
)
1313

14-
func (d *Differ) diffMap(path []string, a, b reflect.Value) error {
14+
func (d *Differ) diffMap(path []string, a, b reflect.Value, parent interface{}) error {
1515
if a.Kind() == reflect.Invalid {
1616
return d.mapValues(CREATE, path, b)
1717
}

diff_slice.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
"reflect"
99
)
1010

11-
func (d *Differ) diffSlice(path []string, a, b reflect.Value) error {
11+
func (d *Differ) diffSlice(path []string, a, b reflect.Value, parent interface{}) error {
1212
if a.Kind() == reflect.Invalid {
1313
d.cl.Add(CREATE, path, nil, exportInterface(b))
1414
return nil

diff_struct.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
"time"
1010
)
1111

12-
func (d *Differ) diffStruct(path []string, a, b reflect.Value) error {
12+
func (d *Differ) diffStruct(path []string, a, b reflect.Value, parent interface{}) error {
1313
if AreType(a, b, reflect.TypeOf(time.Time{})) {
1414
return d.diffTime(path, a, b)
1515
}

diff_test.go

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package diff_test
66

77
import (
88
"reflect"
9+
"strings"
910
"sync"
1011
"testing"
1112
"time"
@@ -907,7 +908,7 @@ func (o *testTypeDiffer) InsertParentDiffer(dfunc func(path []string, a, b refle
907908
func (o *testTypeDiffer) Match(a, b reflect.Value) bool {
908909
return diff.AreType(a, b, reflect.TypeOf(testType("")))
909910
}
910-
func (o *testTypeDiffer) Diff(cl *diff.Changelog, path []string, a, b reflect.Value) error {
911+
func (o *testTypeDiffer) Diff(dt diff.DiffType, df diff.DiffFunc, cl *diff.Changelog, path []string, a, b reflect.Value, parent interface{}) error {
911912
if a.String() != "custom" && b.String() != "match" {
912913
cl.Add(diff.UPDATE, path, a.Interface(), b.Interface())
913914
}
@@ -944,6 +945,54 @@ func TestCustomDiffer(t *testing.T) {
944945
assert.Len(t, cl, 1)
945946
}
946947

948+
type testStringInterceptorDiffer struct {
949+
DiffFunc (func(path []string, a, b reflect.Value, p interface{}) error)
950+
}
951+
952+
func (o *testStringInterceptorDiffer) InsertParentDiffer(dfunc func(path []string, a, b reflect.Value, p interface{}) error) {
953+
o.DiffFunc = dfunc
954+
}
955+
956+
func (o *testStringInterceptorDiffer) Match(a, b reflect.Value) bool {
957+
return diff.AreType(a, b, reflect.TypeOf(testType("")))
958+
}
959+
func (o *testStringInterceptorDiffer) Diff(dt diff.DiffType, df diff.DiffFunc, cl *diff.Changelog, path []string, a, b reflect.Value, parent interface{}) error {
960+
if dt.String() == "STRING" {
961+
// intercept the data
962+
aValue, aOk := a.Interface().(testType)
963+
bValue, bOk := b.Interface().(testType)
964+
965+
if aOk && bOk {
966+
if aValue == "avalue" {
967+
aValue = testType(strings.ToUpper(string(aValue)))
968+
a = reflect.ValueOf(aValue)
969+
}
970+
971+
if bValue == "bvalue" {
972+
bValue = testType(strings.ToUpper(string(aValue)))
973+
b = reflect.ValueOf(bValue)
974+
}
975+
}
976+
}
977+
978+
// continue the diff logic passing the updated a/b values
979+
return df(path, a, b, parent)
980+
}
981+
982+
func TestStringInterceptorDiffer(t *testing.T) {
983+
d, err := diff.NewDiffer(
984+
diff.CustomValueDiffers(
985+
&testStringInterceptorDiffer{},
986+
),
987+
)
988+
require.Nil(t, err)
989+
990+
cl, err := d.Diff(testType("avalue"), testType("bvalue"))
991+
require.Nil(t, err)
992+
993+
assert.Len(t, cl, 0)
994+
}
995+
947996
type RecursiveTestStruct struct {
948997
Id int
949998
Children []RecursiveTestStruct
@@ -961,7 +1010,7 @@ func (o *recursiveTestStructDiffer) Match(a, b reflect.Value) bool {
9611010
return diff.AreType(a, b, reflect.TypeOf(RecursiveTestStruct{}))
9621011
}
9631012

964-
func (o *recursiveTestStructDiffer) Diff(cl *diff.Changelog, path []string, a, b reflect.Value) error {
1013+
func (o *recursiveTestStructDiffer) Diff(dt diff.DiffType, df diff.DiffFunc, cl *diff.Changelog, path []string, a, b reflect.Value, parent interface{}) error {
9651014
if a.Kind() == reflect.Invalid {
9661015
cl.Add(diff.CREATE, path, nil, b.Interface())
9671016
return nil

0 commit comments

Comments
 (0)