From 73468e84b658283bc93d5f808dcce642afed0ead Mon Sep 17 00:00:00 2001 From: Luno Date: Sat, 27 Jun 2026 18:26:52 +0900 Subject: [PATCH] =?UTF-8?q?Fix=20#3:=20=E3=82=B3=E3=83=A1=E3=83=B3?= =?UTF-8?q?=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/dataxl/main.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/cmd/dataxl/main.go b/cmd/dataxl/main.go index af25a8c..9d39a09 100644 --- a/cmd/dataxl/main.go +++ b/cmd/dataxl/main.go @@ -95,6 +95,9 @@ Notes: } var out []byte + // Use table as the common representation whenever either side is a + // spreadsheet-like format. Structured formats can then share the same + // flatten/restore path for CSV, TSV, and XLSX. if isTabular(opt.from) && isTabular(opt.to) { t, err := parseTable(input, opt.from, opt.sheet) if err != nil { @@ -366,6 +369,8 @@ func writeXLSX(t table, sheet string) ([]byte, error) { } } if len(t.Header) > 0 { + // The generated workbook is meant for editing, so keep the header row + // visible and visually distinct. end, _ := excelize.CoordinatesToCellName(len(t.Header), 1) style, _ := f.NewStyle(&excelize.Style{Font: &excelize.Font{Bold: true}}) _ = f.SetCellStyle(sheet, "A1", end, style) @@ -401,6 +406,7 @@ func valueToTable(value any) table { } flatRows = append(flatRows, flat) } + // Stable column ordering keeps generated CSV/TSV/XLSX diffs predictable. sort.Strings(header) rows := make([][]string, 0, len(flatRows)) for _, flat := range flatRows { @@ -418,6 +424,8 @@ func recordsFromValue(value any) []any { case []any: return v case map[string]any: + // Common wrapper keys let TOML and object-shaped inputs represent a + // table without adding a format-specific flag. for _, key := range []string{"rows", "records", "items"} { if rows, ok := v[key].([]any); ok { return rows @@ -429,6 +437,8 @@ func recordsFromValue(value any) []any { } } +// flatten converts nested values into spreadsheet-safe column paths such as +// user.name and items[0].sku. func flatten(prefix string, value any, out map[string]string) { switch v := value.(type) { case map[string]any: @@ -485,6 +495,8 @@ func scalarString(value any) string { } } +// tableToRecords restores each row by interpreting header cells as path +// expressions. Empty headers are ignored so spare spreadsheet columns are safe. func tableToRecords(t table) []map[string]any { var records []map[string]any for _, row := range t.Rows { @@ -501,6 +513,8 @@ func tableToRecords(t table) []map[string]any { return records } +// parseCell keeps spreadsheet round trips useful while avoiding broad type +// inference that could surprise users editing IDs or codes. func parseCell(s string) any { s = strings.TrimSpace(s) if s == "" { @@ -523,6 +537,8 @@ func parseCell(s string) any { var pathTokenRE = regexp.MustCompile(`([^\.\[\]]+)|\[(\d+)\]`) +// setPath creates maps and slices as needed for dotted and indexed header +// paths. Invalid intermediate shapes are left unchanged instead of guessing. func setPath(root map[string]any, path string, value any) { tokens := parsePath(path) if len(tokens) == 0 {