Repository: tableauio/tableau
Pull request #166 (2024-11-26 13:20:48)

feat(patch): add loading mode option


Patch:
diff --git a/load/load.go b/load/load.go
index 4f2fc67..8c126c8 100644
--- a/load/load.go
+++ b/load/load.go
@@ -40,7 +40,7 @@ func Load(msg proto.Message, dir string, fmt format.Format, options ...Option) e
 		path = filepath.Join(dir, name+format.Format2Ext(fmt))
 	}
 	_, sheetOpts := confgen.ParseMessageOptions(md)
-	if sheetOpts.Patch != tableaupb.Patch_PATCH_NONE {
+	if sheetOpts.Patch != tableaupb.Patch_PATCH_NONE && opts.Mode != ModeOnlyMain {
 		return loadWithPatch(msg, path, fmt, sheetOpts.Patch, opts)
 	}
 	return load(msg, path, fmt, opts)
@@ -71,6 +71,10 @@ func loadWithPatch(msg proto.Message, path string, fmt format.Format, patch tabl
 		}
 	}
 	if len(existedPatchPaths) == 0 {
+		if opts.Mode == ModeOnlyPatch {
+			// just returns empty message when main file ingored and no valid patch file provided.
+			return nil
+		}
 		// no valid patch path provided, then just load from the "main" file.
 		return load(msg, path, fmt, opts)
 	}
@@ -83,9 +87,11 @@ func loadWithPatch(msg proto.Message, path string, fmt format.Format, patch tabl
 			return err
 		}
 	case tableaupb.Patch_PATCH_MERGE:
-		// load msg from the "main" file
-		if err := load(msg, path, fmt, opts); err != nil {
-			return err
+		if opts.Mode != ModeOnlyPatch {
+			// load msg from the "main" file
+			if err := load(msg, path, fmt, opts); err != nil {
+				return err
+			}
 		}
 		patchMsg := msg.ProtoReflect().New().Interface()
 		// load patchMsg from each "patch" file
diff --git a/load/options.go b/load/options.go
index c2ecb29..eec1459 100644
--- a/load/options.go
+++ b/load/options.go
@@ -46,8 +46,20 @@ type Options struct {
 	//
 	// Default: nil.
 	PatchDirs []string
+	// Mode specifies the loading mode for config patching.
+	//
+	// Default: ModeDefault.
+	Mode LoadMode
 }
 
+type LoadMode int
+
+const (
+	ModeDefault   LoadMode = iota // Load all related files
+	ModeOnlyMain                  // Only load the main file
+	ModeOnlyPatch                 // Only load the patch files
+)
+
 // FilterFunc filter in messagers if returned value is true.
 //
 // NOTE: name is the protobuf message name, e.g.: "message ItemConf{...}".
@@ -132,3 +144,12 @@ func PatchDirs(dirs ...string) Option {
 		opts.PatchDirs = dirs
 	}
 }
+
+// Mode specifies the loading mode for config patching.
+//
+// NOTE: only JSON, Bin, and Text formats are supported.
+func Mode(mode LoadMode) Option {
+	return func(opts *Options) {
+		opts.Mode = mode
+	}
+}


Test patch:
diff --git a/load/load_test.go b/load/load_test.go
index 2c0c8cf..9805297 100644
--- a/load/load_test.go
+++ b/load/load_test.go
@@ -335,6 +335,33 @@ func TestLoadWithPatch(t *testing.T) {
 				options: []Option{PatchPaths(map[string][]string{"PatchMergeConf": {"../testdata/unittest/patchconf/PatchMergeConf.json", "../testdata/unittest/patchconf2/PatchMergeConf.json", "some/path/that/does/not/exist"}})},
 			},
 		},
+		{
+			name: "ModeOnlyMain",
+			args: args{
+				msg:     &unittestpb.PatchMergeConf{},
+				dir:     "../testdata/unittest/conf/",
+				fmt:     format.JSON,
+				options: []Option{PatchPaths(map[string][]string{"PatchMergeConf": {"../testdata/unittest/patchconf/PatchMergeConf.json", "../testdata/unittest/patchconf2/PatchMergeConf.json"}}), Mode(ModeOnlyMain)},
+			},
+		},
+		{
+			name: "ModeOnlyPatch",
+			args: args{
+				msg:     &unittestpb.PatchMergeConf{},
+				dir:     "../testdata/unittest/conf/",
+				fmt:     format.JSON,
+				options: []Option{PatchPaths(map[string][]string{"PatchMergeConf": {"../testdata/unittest/patchconf/PatchMergeConf.json", "../testdata/unittest/patchconf2/PatchMergeConf.json"}}), Mode(ModeOnlyPatch)},
+			},
+		},
+		{
+			name: "ModeOnlyPatch-but-no-valid-patch-file",
+			args: args{
+				msg:     &unittestpb.PatchMergeConf{},
+				dir:     "../testdata/unittest/conf/",
+				fmt:     format.JSON,
+				options: []Option{PatchPaths(map[string][]string{"PatchMergeConf": {"some/path/that/does/not/exist"}}), Mode(ModeOnlyPatch)},
+			},
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
diff --git a/testdata/unittest/patchresult/ModeOnlyMain.json b/testdata/unittest/patchresult/ModeOnlyMain.json
new file mode 100644
index 0000000..95b717c
--- /dev/null
+++ b/testdata/unittest/patchresult/ModeOnlyMain.json
@@ -0,0 +1,37 @@
+{
+    "name": "apple",
+    "name2": "apple2",
+    "name3": "apple3",
+    "time": {
+        "start": "2024-10-01T02:10:10Z",
+        "expiry": "3600s"
+    },
+    "priceList": [
+        10,
+        100
+    ],
+    "replacePriceList": [
+        10,
+        100
+    ],
+    "itemMap": {
+        "1": {
+            "id": 1,
+            "num": 10
+        },
+        "2": {
+            "id": 2,
+            "num": 20
+        }
+    },
+    "replaceItemMap": {
+        "1": {
+            "id": 1,
+            "num": 10
+        },
+        "2": {
+            "id": 2,
+            "num": 20
+        }
+    }
+}
\ No newline at end of file
diff --git a/testdata/unittest/patchresult/ModeOnlyPatch-but-no-valid-patch-file.json b/testdata/unittest/patchresult/ModeOnlyPatch-but-no-valid-patch-file.json
new file mode 100644
index 0000000..9e26dfe
--- /dev/null
+++ b/testdata/unittest/patchresult/ModeOnlyPatch-but-no-valid-patch-file.json
@@ -0,0 +1,1 @@
+{}
\ No newline at end of file
diff --git a/testdata/unittest/patchresult/ModeOnlyPatch.json b/testdata/unittest/patchresult/ModeOnlyPatch.json
new file mode 100644
index 0000000..dc4aa74
--- /dev/null
+++ b/testdata/unittest/patchresult/ModeOnlyPatch.json
@@ -0,0 +1,35 @@
+{
+    "name": "orange",
+    "name3": "",
+    "time": {
+        "expiry": "7200s"
+    },
+    "priceList": [
+        20,
+        200
+    ],
+    "replacePriceList": [
+        20,
+        200
+    ],
+    "itemMap": {
+        "1": {
+            "id": 1,
+            "num": 99
+        },
+        "999": {
+            "id": 999,
+            "num": 99900
+        }
+    },
+    "replaceItemMap": {
+        "1": {
+            "id": 1,
+            "num": 99
+        },
+        "999": {
+            "id": 999,
+            "num": 99900
+        }
+    }
+}
\ No newline at end of file