Repository: tableauio/tableau
Pull request #262 (2025-07-04 12:58:38)

fix: errors on seperating keyed index
<img width="1345" alt="企业微信截图_17516339108117" src="https://github.com/user-attachments/assets/2937bf70-a3e2-484c-a2fc-181726b88a3d" />


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/tableauio/tableau/pull/262?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 71.27%. Comparing base [(`01d0caf`)](https://app.codecov.io/gh/tableauio/tableau/commit/01d0caf957d1d91259f8d9484cc6f2884b110a9d?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio) to head [(`0c43aa0`)](https://app.codecov.io/gh/tableauio/tableau/commit/0c43aa03f316d0f72235156e1d533b046887a2bf?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master     #262      +/-   ##
==========================================
- Coverage   71.30%   71.27%   -0.04%     
==========================================
  Files          83       83              
  Lines       10350    10339      -11     
==========================================
- Hits         7380     7369      -11     
  Misses       2404     2404              
  Partials      566      566              
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/tableauio/tableau/pull/262?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>

Patch:
diff --git a/internal/importer/book/sheet.go b/internal/importer/book/sheet.go
index 37c72aa..2de684c 100644
--- a/internal/importer/book/sheet.go
+++ b/internal/importer/book/sheet.go
@@ -150,39 +150,23 @@ func parseIndexes(str string) []string {
 	}
 
 	var indexes []string
-	var hasGroupLeft, hasGroupRight bool
-	start := 0
-	for i := 0; i <= len(str); i++ {
-		if i == len(str) {
-			indexes = appendIndex(indexes, str, start, i)
-			break
+	splits := strings.Split(str, ",")
+	curr := ""
+	for _, s := range splits {
+		if curr == "" {
+			curr = s
+		} else {
+			curr += "," + s
 		}
-
-		switch str[i] {
-		case '(':
-			hasGroupLeft = true
-		case ')':
-			hasGroupRight = true
-		case ',':
-			if (!hasGroupLeft && !hasGroupRight) || (hasGroupLeft && hasGroupRight) {
-				indexes = appendIndex(indexes, str, start, i)
-
-				start = i + 1 // skip ',' to next rune
-				hasGroupLeft, hasGroupRight = false, false
-			}
+		if strings.Count(curr, "(") == strings.Count(curr, ")") &&
+			strings.Count(curr, "<") == strings.Count(curr, ">") {
+			indexes = append(indexes, strings.TrimSpace(curr))
+			curr = ""
 		}
 	}
 	return indexes
 }
 
-func appendIndex(indexes []string, str string, start, end int) []string {
-	index := strings.TrimSpace(str[start:end])
-	if index != "" {
-		indexes = append(indexes, index)
-	}
-	return indexes
-}
-
 func MetabookOptions() *tableaupb.WorkbookOptions {
 	return &tableaupb.WorkbookOptions{
 		Namerow: 1,


Test patch:
diff --git a/internal/importer/book/sheet_test.go b/internal/importer/book/sheet_test.go
index 91b5466..b4409a8 100644
--- a/internal/importer/book/sheet_test.go
+++ b/internal/importer/book/sheet_test.go
@@ -73,6 +73,48 @@ func Test_parseIndexes(t *testing.T) {
 			},
 			want: []string{"ID", "(ID, Type)@Item"},
 		},
+		{
+			name: "one keyed index",
+			args: args{
+				str: "ID<Name>",
+			},
+			want: []string{"ID<Name>"},
+		},
+		{
+			name: "one multi-column keyed index",
+			args: args{
+				str: "(ID, Type)<Name>",
+			},
+			want: []string{"(ID, Type)<Name>"},
+		},
+		{
+			name: "one named multi-column keyed index",
+			args: args{
+				str: "(ID, Type)<Name>@Item",
+			},
+			want: []string{"(ID, Type)<Name>@Item"},
+		},
+		{
+			name: "one named multi-column multi-keyed index",
+			args: args{
+				str: "(ID, Type)<Name, Alias>@Item",
+			},
+			want: []string{"(ID, Type)<Name, Alias>@Item"},
+		},
+		{
+			name: "one single-column index and one named multi-column multi-keyed index",
+			args: args{
+				str: "ID,(ID, Type)<Name, Alias>@Item",
+			},
+			want: []string{"ID", "(ID, Type)<Name, Alias>@Item"},
+		},
+		{
+			name: "one single-column index and one named multi-column multi-keyed index with extra space separated",
+			args: args{
+				str: "ID, (ID, Type)<Name, Alias>@Item",
+			},
+			want: []string{"ID", "(ID, Type)<Name, Alias>@Item"},
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {