Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
table: RenderTSV to render in tab-separated-values format (#277)
- Loading branch information
1 parent
05c0986
commit 17050b7
Showing
4 changed files
with
268 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package table | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
) | ||
|
||
func (t *Table) RenderTSV() string { | ||
t.initForRender() | ||
|
||
var out strings.Builder | ||
|
||
if t.numColumns > 0 { | ||
if t.title != "" { | ||
out.WriteString(t.title) | ||
} | ||
|
||
if t.autoIndex && len(t.rowsHeader) == 0 { | ||
t.tsvRenderRow(&out, t.getAutoIndexColumnIDs(), renderHint{isAutoIndexRow: true, isHeaderRow: true}) | ||
} | ||
|
||
t.tsvRenderRows(&out, t.rowsHeader, renderHint{isHeaderRow: true}) | ||
t.tsvRenderRows(&out, t.rows, renderHint{}) | ||
t.tsvRenderRows(&out, t.rowsFooter, renderHint{isFooterRow: true}) | ||
|
||
if t.caption != "" { | ||
out.WriteRune('\n') | ||
out.WriteString(t.caption) | ||
} | ||
} | ||
|
||
return t.render(&out) | ||
} | ||
|
||
func (t *Table) tsvRenderRow(out *strings.Builder, row rowStr, hint renderHint) { | ||
if out.Len() > 0 { | ||
out.WriteRune('\n') | ||
} | ||
|
||
for idx, col := range row { | ||
if idx == 0 && t.autoIndex { | ||
if hint.isRegularRow() { | ||
out.WriteString(fmt.Sprint(hint.rowNumber)) | ||
} | ||
out.WriteRune('\t') | ||
} | ||
|
||
if idx > 0 { | ||
out.WriteRune('\t') | ||
} | ||
|
||
if strings.ContainsAny(col, "\t\n\"") || strings.Contains(col, " ") { | ||
out.WriteString(fmt.Sprintf("\"%s\"", t.tsvFixDoubleQuotes(col))) | ||
} else { | ||
out.WriteString(col) | ||
} | ||
} | ||
|
||
for colIdx := len(row); colIdx < t.numColumns; colIdx++ { | ||
out.WriteRune('\t') | ||
} | ||
} | ||
|
||
func (t *Table) tsvFixDoubleQuotes(str string) string { | ||
return strings.Replace(str, "\"", "\"\"", -1) | ||
} | ||
|
||
func (t *Table) tsvRenderRows(out *strings.Builder, rows []rowStr, hint renderHint) { | ||
for idx, row := range rows { | ||
hint.rowNumber = idx + 1 | ||
t.tsvRenderRow(out, row, hint) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
package table | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
) | ||
|
||
func TestTable_RenderTSV(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
tw func() Writer | ||
output string | ||
}{ | ||
{ | ||
tw: func() Writer { | ||
tw := NewWriter() | ||
tw.AppendHeader(testHeader) | ||
tw.AppendRows(testRows) | ||
tw.AppendRow(testRowMultiLine) | ||
tw.AppendRow(testRowTabs) | ||
tw.AppendRow(testRowDoubleQuotes) | ||
tw.AppendFooter(testFooter) | ||
tw.SetCaption(testCaption) | ||
tw.SetTitle(testTitle1) | ||
return tw | ||
}, | ||
output: ` | ||
Game of Thrones | ||
# First Name Last Name Salary | ||
1 Arya Stark 3000 | ||
20 Jon Snow 2000 You know nothing, Jon Snow! | ||
300 Tyrion Lannister 5000 | ||
0 Winter Is 0 "Coming. | ||
The North Remembers! | ||
This is known." | ||
0 Valar Morghulis 0 "Faceless Men" | ||
0 Valar Morghulis 0 "Faceless""Men" | ||
Total 10000 | ||
A Song of Ice and Fire`, | ||
}, | ||
{ | ||
name: "Auto index", | ||
tw: func() Writer { | ||
tw := NewWriter() | ||
for rowIdx := 0; rowIdx < 10; rowIdx++ { | ||
row := make(Row, 10) | ||
for colIdx := 0; colIdx < 10; colIdx++ { | ||
row[colIdx] = fmt.Sprintf("%s%d", AutoIndexColumnID(colIdx), rowIdx+1) | ||
} | ||
tw.AppendRow(row) | ||
} | ||
for rowIdx := 0; rowIdx < 1; rowIdx++ { | ||
row := make(Row, 10) | ||
for colIdx := 0; colIdx < 10; colIdx++ { | ||
row[colIdx] = AutoIndexColumnID(colIdx) + "F" | ||
} | ||
tw.AppendFooter(row) | ||
} | ||
tw.SetAutoIndex(true) | ||
tw.SetStyle(StyleLight) | ||
return tw | ||
}, | ||
output: ` | ||
A B C D E F G H I J | ||
1 A1 B1 C1 D1 E1 F1 G1 H1 I1 J1 | ||
2 A2 B2 C2 D2 E2 F2 G2 H2 I2 J2 | ||
3 A3 B3 C3 D3 E3 F3 G3 H3 I3 J3 | ||
4 A4 B4 C4 D4 E4 F4 G4 H4 I4 J4 | ||
5 A5 B5 C5 D5 E5 F5 G5 H5 I5 J5 | ||
6 A6 B6 C6 D6 E6 F6 G6 H6 I6 J6 | ||
7 A7 B7 C7 D7 E7 F7 G7 H7 I7 J7 | ||
8 A8 B8 C8 D8 E8 F8 G8 H8 I8 J8 | ||
9 A9 B9 C9 D9 E9 F9 G9 H9 I9 J9 | ||
10 A10 B10 C10 D10 E10 F10 G10 H10 I10 J10 | ||
AF BF CF DF EF FF GF HF IF JF`, | ||
}, | ||
{ | ||
name: "Empty", | ||
tw: func() Writer { | ||
tw := NewWriter() | ||
return tw | ||
}, | ||
output: ``, | ||
}, | ||
{ | ||
name: "Every column hidden", | ||
tw: func() Writer { | ||
tw := NewWriter() | ||
tw.AppendHeader(testHeader) | ||
tw.AppendRows(testRows) | ||
tw.AppendFooter(testFooter) | ||
tw.SortBy([]SortBy{ | ||
{Name: "Salary", Mode: DscNumeric}, | ||
}) | ||
tw.SetColumnConfigs(generateColumnConfigsWithHiddenColumns([]int{0, 1, 2, 3, 4})) | ||
return tw | ||
}, | ||
output: ``, | ||
}, | ||
{ | ||
name: "First column hidden", | ||
tw: func() Writer { | ||
tw := NewWriter() | ||
tw.AppendHeader(testHeader) | ||
tw.AppendRows(testRows) | ||
tw.AppendFooter(testFooter) | ||
tw.SortBy([]SortBy{ | ||
{Name: "Salary", Mode: DscNumeric}, | ||
}) | ||
tw.SetColumnConfigs(generateColumnConfigsWithHiddenColumns([]int{0})) | ||
return tw | ||
}, | ||
output: ` | ||
First Name Last Name Salary | ||
>>Tyrion Lannister<< 5013 | ||
>>Arya Stark<< 3013 | ||
>>Jon Snow<< 2013 ~You know nothing, Jon Snow!~ | ||
Total 10000 `, | ||
}, | ||
{ | ||
name: "Column hidden in the middle", | ||
tw: func() Writer { | ||
tw := NewWriter() | ||
tw.AppendHeader(testHeader) | ||
tw.AppendRows(testRows) | ||
tw.AppendFooter(testFooter) | ||
tw.SortBy([]SortBy{ | ||
{Name: "Salary", Mode: DscNumeric}, | ||
}) | ||
tw.SetColumnConfigs(generateColumnConfigsWithHiddenColumns([]int{1})) | ||
return tw | ||
}, | ||
output: ` | ||
# Last Name Salary | ||
307 Lannister<< 5013 | ||
8 Stark<< 3013 | ||
27 Snow<< 2013 ~You know nothing, Jon Snow!~ | ||
Total 10000 `, | ||
}, | ||
{ | ||
name: "Last column hidden", | ||
tw: func() Writer { | ||
tw := NewWriter() | ||
tw.AppendHeader(testHeader) | ||
tw.AppendRows(testRows) | ||
tw.AppendFooter(testFooter) | ||
tw.SortBy([]SortBy{ | ||
{Name: "Salary", Mode: DscNumeric}, | ||
}) | ||
tw.SetColumnConfigs(generateColumnConfigsWithHiddenColumns([]int{4})) | ||
return tw | ||
}, | ||
output: ` | ||
# First Name Last Name Salary | ||
307 >>Tyrion Lannister<< 5013 | ||
8 >>Arya Stark<< 3013 | ||
27 >>Jon Snow<< 2013 | ||
Total 10000`, | ||
}, | ||
{ | ||
name: "Sorted", | ||
tw: func() Writer { | ||
tw := NewWriter() | ||
tw.AppendHeader(testHeader) | ||
tw.AppendRows(testRows) | ||
tw.AppendRow(Row{11, "Sansa", "Stark", 6000}) | ||
tw.AppendFooter(testFooter) | ||
tw.SortBy([]SortBy{{Name: "Last Name", Mode: Asc}, {Name: "First Name", Mode: Asc}}) | ||
return tw | ||
}, | ||
output: ` | ||
# First Name Last Name Salary | ||
300 Tyrion Lannister 5000 | ||
20 Jon Snow 2000 You know nothing, Jon Snow! | ||
1 Arya Stark 3000 | ||
11 Sansa Stark 6000 | ||
Total 10000 `, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
output := tt.tw().RenderTSV() | ||
compareOutput(t, output, tt.output) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters