From cbe7886e3d90f6616cc97a7279d609144ff07534 Mon Sep 17 00:00:00 2001
From: Anton Sattarov <dirty.mew@gmail.com>
Date: Wed, 18 Oct 2023 10:57:41 +0200
Subject: [PATCH] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?=
 =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B0=20?=
 =?UTF-8?q?=D0=B2=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B8=20keepSourc?=
 =?UTF-8?q?e=20=D0=BF=D0=B0=D0=BA=D0=B5=D1=82=D0=B0=20schema=20-=20=D0=B2?=
 =?UTF-8?q?=D1=81=D0=B5=D0=B3=D0=B4=D0=B0=20=D0=B2=D0=BE=D0=B7=D0=B2=D1=80?=
 =?UTF-8?q?=D0=B0=D1=89=D0=B0=D0=BB=D1=81=D1=8F=20=D1=84=D0=BB=D0=B0=D0=B3?=
 =?UTF-8?q?=20`=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD=D0=BE`?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 perxis-proto                 |   2 +-
 pkg/schema/walk/fn.go        |   5 ++
 pkg/schema/walk/walk_test.go | 115 ++++++++++++++++++++++++++++++++++-
 3 files changed, 119 insertions(+), 3 deletions(-)

diff --git a/perxis-proto b/perxis-proto
index 81c96784..8c2633f8 160000
--- a/perxis-proto
+++ b/perxis-proto
@@ -1 +1 @@
-Subproject commit 81c967842f55811b459e455572703631712d7d86
+Subproject commit 8c2633f87320a29c7abd9389cedda60a64f88bfa
diff --git a/pkg/schema/walk/fn.go b/pkg/schema/walk/fn.go
index c3eaf35c..9b1d4f22 100644
--- a/pkg/schema/walk/fn.go
+++ b/pkg/schema/walk/fn.go
@@ -1,10 +1,15 @@
 package walk
 
+import "reflect"
+
 func GenericMerge(c *WalkContext) (err error) {
 	return
 }
 
 func KeepSrc(c *WalkContext) (err error) {
+	if reflect.DeepEqual(c.Src, c.Dst) {
+		return
+	}
 	c.Dst = c.Src
 	c.Changed = true
 	return
diff --git a/pkg/schema/walk/walk_test.go b/pkg/schema/walk/walk_test.go
index e20adb46..3fd01bd5 100644
--- a/pkg/schema/walk/walk_test.go
+++ b/pkg/schema/walk/walk_test.go
@@ -46,6 +46,54 @@ func TestWalker_DataWalk(t *testing.T) {
 		wantErr     bool
 	}{
 		{"generic",
+			&WalkConfig{
+				Fields: map[string]FieldConfig{},
+			},
+			map[string]interface{}{
+				"a": "src_a",
+				"b": "src_b",
+				"obj1": map[string]interface{}{
+					"a": "src_obj1_a",
+					"b": "src_obj1_b",
+					"obj2": map[string]interface{}{
+						"a": "dst_obj1_obj2_a",
+					},
+					"obj3": map[string]interface{}{
+						"e": "dst_obj1_obj3_e",
+					},
+				},
+				"inline_str_1": "src_inline_1",
+				"inline_str_2": "src_inline_2",
+				"slice":        []interface{}{"src_s1", "src_s2"},
+			},
+			map[string]interface{}{
+				"a":                 "dst_a",
+				"field_not_extists": "remove",
+				"obj1": map[string]interface{}{
+					"a": "dst_obj1_a",
+					"obj2": map[string]interface{}{
+						"a": "dst_obj1_obj2_a",
+					},
+				},
+				"inline_str_1": "dst_inline_1",
+				"inline_str_2": "dst_inline_2",
+				"slice":        []interface{}{"dst_s1", "dst_s2", "dst_s3"},
+			},
+			map[string]interface{}{
+				"a": "dst_a",
+				"obj1": map[string]interface{}{
+					"a": "dst_obj1_a",
+					"obj2": map[string]interface{}{
+						"a": "dst_obj1_obj2_a",
+					},
+				},
+				"inline_str_1": "dst_inline_1",
+				"inline_str_2": "dst_inline_2",
+				"slice":        []interface{}{"dst_s1", "dst_s2", "dst_s3"},
+			},
+			false, false,
+		},
+		{"keep src changed",
 			&WalkConfig{
 				Fields: map[string]FieldConfig{
 					"obj1.a":       {Fn: KeepSrc},
@@ -96,6 +144,68 @@ func TestWalker_DataWalk(t *testing.T) {
 				"inline_str_2": "src_inline_2",
 				"slice":        []interface{}{"dst_s1", "src_s2", "dst_s3"},
 			},
+			true, false,
+		},
+		{"keep src not changed",
+			&WalkConfig{
+				Fields: map[string]FieldConfig{
+					"obj1.a":       {Fn: KeepSrc},
+					"slice.1":      {Fn: KeepSrc},
+					"inline_str_1": {Fn: KeepSrc},
+					"inline_str_2": {Fn: KeepSrc},
+				},
+			},
+			map[string]interface{}{
+				"a": "src_a",
+				"b": "src_b",
+				"obj1": map[string]interface{}{
+					"a": "src_obj1_a",
+					"b": "src_obj1_b",
+					"obj2": map[string]interface{}{
+						"a": "dst_obj1_obj2_a",
+					},
+					"obj3": map[string]interface{}{
+						"e": "dst_obj1_obj3_e",
+					},
+				},
+				"inline_str_1": "src_inline_1",
+				"inline_str_2": "src_inline_2",
+				"slice":        []interface{}{"src_s1", "src_s2"},
+			},
+			map[string]interface{}{
+				"a": "src_a",
+				"b": "src_b",
+				"obj1": map[string]interface{}{
+					"a": "src_obj1_a",
+					"b": "src_obj1_b",
+					"obj2": map[string]interface{}{
+						"a": "dst_obj1_obj2_a",
+					},
+					"obj3": map[string]interface{}{
+						"e": "dst_obj1_obj3_e",
+					},
+				},
+				"inline_str_1": "src_inline_1",
+				"inline_str_2": "src_inline_2",
+				"slice":        []interface{}{"src_s1", "src_s2"},
+			},
+			map[string]interface{}{
+				"a": "src_a",
+				"b": "src_b",
+				"obj1": map[string]interface{}{
+					"a": "src_obj1_a",
+					"b": "src_obj1_b",
+					"obj2": map[string]interface{}{
+						"a": "dst_obj1_obj2_a",
+					},
+					"obj3": map[string]interface{}{
+						"e": "dst_obj1_obj3_e",
+					},
+				},
+				"inline_str_1": "src_inline_1",
+				"inline_str_2": "src_inline_2",
+				"slice":        []interface{}{"src_s1", "src_s2"},
+			},
 			false, false,
 		},
 	}
@@ -103,11 +213,12 @@ func TestWalker_DataWalk(t *testing.T) {
 		t.Run(tt.name, func(t *testing.T) {
 			m := NewWalker(s, tt.config)
 			dst := tt.dst
-			res, _, err := m.DataWalk(context.Background(), dst, tt.src)
-			assert.Equal(t, tt.res, res)
+			res, chg, err := m.DataWalk(context.Background(), dst, tt.src)
 			if tt.wantErr {
 				require.Error(t, err)
 			} else {
+				assert.Equal(t, tt.res, res)
+				assert.Equal(t, tt.wantChanged, chg)
 				require.NoError(t, err)
 			}
 		})
-- 
GitLab