diff --git a/list/render.go b/list/render.go index 935416d..b3a72fb 100644 --- a/list/render.go +++ b/list/render.go @@ -6,15 +6,15 @@ import ( ) // Render renders the List in a human-readable "pretty" format. Example: -// * Game Of Thrones -// * Winter -// * Is -// * Coming -// * This -// * Is -// * Known -// * The Dark Tower -// * The Gunslinger +// | * Game Of Thrones +// | * Winter +// | * Is +// | * Coming +// | * This +// | * Is +// | * Known +// | * The Dark Tower +// | * The Gunslinger func (l *List) Render() string { l.initForRender() diff --git a/list/render_html.go b/list/render_html.go index 7b51001..143c6ee 100644 --- a/list/render_html.go +++ b/list/render_html.go @@ -7,23 +7,24 @@ import ( ) // RenderHTML renders the List in the HTML format. Example: -// +// +// func (l *List) RenderHTML() string { l.initForRender() diff --git a/list/render_markdown.go b/list/render_markdown.go index 3c8aab8..9d24f64 100644 --- a/list/render_markdown.go +++ b/list/render_markdown.go @@ -1,15 +1,15 @@ package list // RenderMarkdown renders the List in the Markdown format. Example: -// * Game Of Thrones -// * Winter -// * Is -// * Coming -// * This -// * Is -// * Known -// * The Dark Tower -// * The Gunslinger +// | * Game Of Thrones +// | * Winter +// | * Is +// | * Coming +// | * This +// | * Is +// | * Known +// | * The Dark Tower +// | * The Gunslinger func (l *List) RenderMarkdown() string { // make a copy of the original style and ensure it is restored on exit originalStyle := l.style diff --git a/progress/tracker_sort.go b/progress/tracker_sort.go index da6ac18..aa998b9 100644 --- a/progress/tracker_sort.go +++ b/progress/tracker_sort.go @@ -56,8 +56,8 @@ func (sb sortByMessage) Less(i, j int) bool { return sb[i].message() < sb[j].mes type sortByPercent []*Tracker -func (sb sortByPercent) Len() int { return len(sb) } -func (sb sortByPercent) Swap(i, j int) { sb[i], sb[j] = sb[j], sb[i] } +func (sb sortByPercent) Len() int { return len(sb) } +func (sb sortByPercent) Swap(i, j int) { sb[i], sb[j] = sb[j], sb[i] } func (sb sortByPercent) Less(i, j int) bool { if sb[i].PercentDone() == sb[j].PercentDone() { return sb[i].timeStart.Before(sb[j].timeStart) @@ -67,8 +67,8 @@ func (sb sortByPercent) Less(i, j int) bool { type sortByValue []*Tracker -func (sb sortByValue) Len() int { return len(sb) } -func (sb sortByValue) Swap(i, j int) { sb[i], sb[j] = sb[j], sb[i] } +func (sb sortByValue) Len() int { return len(sb) } +func (sb sortByValue) Swap(i, j int) { sb[i], sb[j] = sb[j], sb[i] } func (sb sortByValue) Less(i, j int) bool { if sb[i].value == sb[j].value { return sb[i].timeStart.Before(sb[j].timeStart) diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..2233099 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,9 @@ +# Define the root directory for sources and tests +sonar.sources = ./ +sonar.tests = ./ + +# Include test files in test scope +sonar.test.inclusions = **/*_test.go + +# Exclude test files from source scope +sonar.exclusions = **/*_test.go diff --git a/table/render.go b/table/render.go index ca4bc99..2e93da5 100644 --- a/table/render.go +++ b/table/render.go @@ -9,15 +9,16 @@ import ( ) // Render renders the Table in a human-readable "pretty" format. Example: -// ┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐ -// │ # │ FIRST NAME │ LAST NAME │ SALARY │ │ -// ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤ -// │ 1 │ Arya │ Stark │ 3000 │ │ -// │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │ -// │ 300 │ Tyrion │ Lannister │ 5000 │ │ -// ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤ -// │ │ │ TOTAL │ 10000 │ │ -// └─────┴────────────┴───────────┴────────┴─────────────────────────────┘ +// +// ┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐ +// │ # │ FIRST NAME │ LAST NAME │ SALARY │ │ +// ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤ +// │ 1 │ Arya │ Stark │ 3000 │ │ +// │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │ +// │ 300 │ Tyrion │ Lannister │ 5000 │ │ +// ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤ +// │ │ │ TOTAL │ 10000 │ │ +// └─────┴────────────┴───────────┴────────┴─────────────────────────────┘ func (t *Table) Render() string { t.initForRender() diff --git a/table/render_automerge_test.go b/table/render_automerge_test.go new file mode 100644 index 0000000..3781937 --- /dev/null +++ b/table/render_automerge_test.go @@ -0,0 +1,765 @@ +package table + +import ( + "testing" + + "github.com/jedib0t/go-pretty/v6/text" +) + +func TestTable_Render_AutoMerge(t *testing.T) { + rcAutoMerge := RowConfig{AutoMerge: true} + + t.Run("columns only", func(t *testing.T) { + tw := NewWriter() + tw.AppendHeader(Row{"Node IP", "Pods", "Namespace", "Container", "RCE\nEXE", "RCE\nRUN"}) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 1", "Y", "Y"}) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 2", "Y", "N"}) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1B", "C 3", "N", "N"}) + tw.AppendRow(Row{"a.a.a.a", "Pod 1B", "NS 2", "C 4", "N", "N"}) + tw.AppendRow(Row{"a.a.a.a", "Pod 1B", "NS 2", "C 5", "Y", "N"}) + tw.AppendRow(Row{"b.b.b.b", "Pod 2", "NS 3", "C 6", "Y", "Y"}) + tw.AppendRow(Row{"b.b.b.b", "Pod 2", "NS 3", "C 7", "Y", "Y"}) + tw.AppendFooter(Row{"", "", "", 7, 5, 3}) + tw.SetAutoIndex(true) + tw.SetColumnConfigs([]ColumnConfig{ + {Number: 1, AutoMerge: true}, + {Number: 2, AutoMerge: true}, + {Number: 3, AutoMerge: true}, + {Number: 4, AutoMerge: true}, + {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter}, + {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter}, + }) + tw.SetStyle(StyleLight) + tw.Style().Options.SeparateRows = true + + compareOutput(t, tw.Render(), ` +┌───┬─────────┬────────┬───────────┬───────────┬─────┬─────┐ +│ │ NODE IP │ PODS │ NAMESPACE │ CONTAINER │ RCE │ RCE │ +│ │ │ │ │ │ EXE │ RUN │ +├───┼─────────┼────────┼───────────┼───────────┼─────┼─────┤ +│ 1 │ a.a.a.a │ Pod 1A │ NS 1A │ C 1 │ Y │ Y │ +├───┤ │ │ ├───────────┼─────┼─────┤ +│ 2 │ │ │ │ C 2 │ Y │ N │ +├───┤ │ ├───────────┼───────────┼─────┼─────┤ +│ 3 │ │ │ NS 1B │ C 3 │ N │ N │ +├───┤ ├────────┼───────────┼───────────┼─────┼─────┤ +│ 4 │ │ Pod 1B │ NS 2 │ C 4 │ N │ N │ +├───┤ │ │ ├───────────┼─────┼─────┤ +│ 5 │ │ │ │ C 5 │ Y │ N │ +├───┼─────────┼────────┼───────────┼───────────┼─────┼─────┤ +│ 6 │ b.b.b.b │ Pod 2 │ NS 3 │ C 6 │ Y │ Y │ +├───┤ │ │ ├───────────┼─────┼─────┤ +│ 7 │ │ │ │ C 7 │ Y │ Y │ +├───┼─────────┼────────┼───────────┼───────────┼─────┼─────┤ +│ │ │ │ │ 7 │ 5 │ 3 │ +└───┴─────────┴────────┴───────────┴───────────┴─────┴─────┘`) + }) + + t.Run("columns only with hidden columns", func(t *testing.T) { + tw := NewWriter() + tw.AppendHeader(Row{"Node IP", "Pods", "Namespace", "Container", "RCE\nEXE", "RCE\nRUN"}) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 1", "Y", "Y"}) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 2", "Y", "N"}) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1B", "C 3", "N", "N"}) + tw.AppendRow(Row{"a.a.a.a", "Pod 1B", "NS 2", "C 4", "Y", "Y"}) + tw.AppendRow(Row{"a.a.a.a", "Pod 1B", "NS 2", "C 5", "Y", "N"}) + tw.AppendRow(Row{"b.b.b.b", "Pod 2", "NS 3", "C 6", "Y", "Y"}) + tw.AppendRow(Row{"b.b.b.b", "Pod 2", "NS 3", "C 7", "Y", "N"}) + tw.AppendFooter(Row{"", "", "", 7, 5, 3}) + tw.SetColumnConfigs([]ColumnConfig{ + {Number: 1, AutoMerge: true}, + {Number: 2, AutoMerge: true}, + {Number: 3, AutoMerge: true}, + {Number: 4, Hidden: true}, + {Number: 5, Hidden: true, Align: text.AlignCenter}, + {Number: 6, Hidden: true, Align: text.AlignCenter}, + }) + tw.SetStyle(StyleLight) + tw.Style().Options.SeparateRows = true + + compareOutput(t, tw.Render(), ` +┌─────────┬────────┬───────────┐ +│ NODE IP │ PODS │ NAMESPACE │ +├─────────┼────────┼───────────┤ +│ a.a.a.a │ Pod 1A │ NS 1A │ +│ │ │ │ +│ │ │ │ +│ │ ├───────────┤ +│ │ │ NS 1B │ +│ ├────────┼───────────┤ +│ │ Pod 1B │ NS 2 │ +│ │ │ │ +│ │ │ │ +├─────────┼────────┼───────────┤ +│ b.b.b.b │ Pod 2 │ NS 3 │ +│ │ │ │ +│ │ │ │ +├─────────┼────────┼───────────┤ +│ │ │ │ +└─────────┴────────┴───────────┘`) + }) + + t.Run("rows only", func(t *testing.T) { + tw := NewWriter() + tw.AppendHeader(Row{"Node IP", "Pods", "Namespace", "Container", "RCE", "RCE"}, rcAutoMerge) + tw.AppendHeader(Row{"", "", "", "", "EXE", "RUN"}) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 1", "Y", "Y"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 2", "Y", "N"}) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1B", "C 3", "N", "N"}) + tw.AppendRow(Row{"a.a.a.a", "Pod 1B", "NS 2", "C 4", "N", "N"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1B", "NS 2", "C 5", "Y", "N"}) + tw.AppendRow(Row{"b.b.b.b", "Pod 2", "NS 3", "C 6", "Y", "Y"}, rcAutoMerge) + tw.AppendRow(Row{"b.b.b.b", "Pod 2", "NS 3", "C 7", "Y", "Y"}, RowConfig{AutoMerge: true, AutoMergeAlign: text.AlignRight}) + tw.AppendFooter(Row{"", "", "", 7, 5, 3}) + tw.SetAutoIndex(true) + tw.SetColumnConfigs([]ColumnConfig{ + {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter}, + {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter}, + }) + tw.SetStyle(StyleLight) + tw.Style().Options.SeparateRows = true + + compareOutput(t, tw.Render(), ` +┌───┬─────────┬────────┬───────────┬───────────┬───────────┐ +│ │ NODE IP │ PODS │ NAMESPACE │ CONTAINER │ RCE │ +│ ├─────────┼────────┼───────────┼───────────┼─────┬─────┤ +│ │ │ │ │ │ EXE │ RUN │ +├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┤ +│ 1 │ a.a.a.a │ Pod 1A │ NS 1A │ C 1 │ Y │ +├───┼─────────┼────────┼───────────┼───────────┼─────┬─────┤ +│ 2 │ a.a.a.a │ Pod 1A │ NS 1A │ C 2 │ Y │ N │ +├───┼─────────┼────────┼───────────┼───────────┼─────┼─────┤ +│ 3 │ a.a.a.a │ Pod 1A │ NS 1B │ C 3 │ N │ N │ +├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┤ +│ 4 │ a.a.a.a │ Pod 1B │ NS 2 │ C 4 │ N │ +├───┼─────────┼────────┼───────────┼───────────┼─────┬─────┤ +│ 5 │ a.a.a.a │ Pod 1B │ NS 2 │ C 5 │ Y │ N │ +├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┤ +│ 6 │ b.b.b.b │ Pod 2 │ NS 3 │ C 6 │ Y │ +├───┼─────────┼────────┼───────────┼───────────┼───────────┤ +│ 7 │ b.b.b.b │ Pod 2 │ NS 3 │ C 7 │ Y │ +├───┼─────────┼────────┼───────────┼───────────┼─────┬─────┤ +│ │ │ │ │ 7 │ 5 │ 3 │ +└───┴─────────┴────────┴───────────┴───────────┴─────┴─────┘`) + }) + + t.Run("rows and columns", func(t *testing.T) { + tw := NewWriter() + tw.AppendHeader(Row{"Node IP", "Pods", "Namespace", "Container", "RCE", "RCE"}, rcAutoMerge) + tw.AppendHeader(Row{"", "", "", "", "EXE", "RUN"}) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 1", "Y", "Y"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 2", "Y", "N"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1B", "C 3", "N", "N"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1B", "NS 2", "C 4", "N", "N"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1B", "NS 2", "C 5", "Y", "N"}, rcAutoMerge) + tw.AppendRow(Row{"b.b.b.b", "Pod 2", "NS 3", "C 6", "Y", "Y"}, rcAutoMerge) + tw.AppendRow(Row{"b.b.b.b", "Pod 2", "NS 3", "C 7", "Y", "Y"}, rcAutoMerge) + tw.AppendFooter(Row{"", "", "", 7, 5, 3}) + tw.SetAutoIndex(true) + tw.SetColumnConfigs([]ColumnConfig{ + {Number: 1, AutoMerge: true}, + {Number: 2, AutoMerge: true}, + {Number: 3, AutoMerge: true}, + {Number: 4, AutoMerge: true}, + {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter}, + {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter}, + }) + tw.SetStyle(StyleLight) + tw.Style().Options.SeparateRows = true + + compareOutput(t, tw.Render(), ` +┌───┬─────────┬────────┬───────────┬───────────┬───────────┐ +│ │ NODE IP │ PODS │ NAMESPACE │ CONTAINER │ RCE │ +│ ├─────────┼────────┼───────────┼───────────┼─────┬─────┤ +│ │ │ │ │ │ EXE │ RUN │ +├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┤ +│ 1 │ a.a.a.a │ Pod 1A │ NS 1A │ C 1 │ Y │ +├───┤ │ │ ├───────────┼─────┬─────┤ +│ 2 │ │ │ │ C 2 │ Y │ N │ +├───┤ │ ├───────────┼───────────┼─────┴─────┤ +│ 3 │ │ │ NS 1B │ C 3 │ N │ +├───┤ ├────────┼───────────┼───────────┼───────────┤ +│ 4 │ │ Pod 1B │ NS 2 │ C 4 │ N │ +├───┤ │ │ ├───────────┼─────┬─────┤ +│ 5 │ │ │ │ C 5 │ Y │ N │ +├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┤ +│ 6 │ b.b.b.b │ Pod 2 │ NS 3 │ C 6 │ Y │ +├───┤ │ │ ├───────────┼───────────┤ +│ 7 │ │ │ │ C 7 │ Y │ +├───┼─────────┼────────┼───────────┼───────────┼─────┬─────┤ +│ │ │ │ │ 7 │ 5 │ 3 │ +└───┴─────────┴────────┴───────────┴───────────┴─────┴─────┘`) + }) + + t.Run("rows and columns no headers or footers", func(t *testing.T) { + tw := NewWriter() + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 1", "Y", "Y"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 2", "Y", "N"}) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1B", "C 3", "N", "N"}) + tw.AppendRow(Row{"a.a.a.a", "Pod 1B", "NS 2", "C 4", "N", "N"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1B", "NS 2", "C 5", "Y", "N"}) + tw.AppendRow(Row{"b.b.b.b", "Pod 2", "NS 3", "C 6", "Y", "Y"}, rcAutoMerge) + tw.AppendRow(Row{"b.b.b.b", "Pod 2", "NS 3", "C 7", "Y", "Y"}, RowConfig{AutoMerge: true, AutoMergeAlign: text.AlignRight}) + tw.SetColumnConfigs([]ColumnConfig{ + {Number: 5, Align: text.AlignCenter, AlignHeader: text.AlignCenter}, + {Number: 6, Align: text.AlignCenter, AlignHeader: text.AlignCenter}, + }) + tw.SetStyle(StyleLight) + tw.Style().Options.SeparateRows = true + + compareOutput(t, tw.Render(), ` +┌─────────┬────────┬───────┬─────┬───────┐ +│ a.a.a.a │ Pod 1A │ NS 1A │ C 1 │ Y │ +├─────────┼────────┼───────┼─────┼───┬───┤ +│ a.a.a.a │ Pod 1A │ NS 1A │ C 2 │ Y │ N │ +├─────────┼────────┼───────┼─────┼───┼───┤ +│ a.a.a.a │ Pod 1A │ NS 1B │ C 3 │ N │ N │ +├─────────┼────────┼───────┼─────┼───┴───┤ +│ a.a.a.a │ Pod 1B │ NS 2 │ C 4 │ N │ +├─────────┼────────┼───────┼─────┼───┬───┤ +│ a.a.a.a │ Pod 1B │ NS 2 │ C 5 │ Y │ N │ +├─────────┼────────┼───────┼─────┼───┴───┤ +│ b.b.b.b │ Pod 2 │ NS 3 │ C 6 │ Y │ +├─────────┼────────┼───────┼─────┼───────┤ +│ b.b.b.b │ Pod 2 │ NS 3 │ C 7 │ Y │ +└─────────┴────────┴───────┴─────┴───────┘`) + }) + + t.Run("rows and columns no headers or footers with auto-index", func(t *testing.T) { + tw := NewWriter() + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 1", "Y", "Y"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 2", "Y", "N"}) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1B", "C 3", "N", "N"}) + tw.AppendRow(Row{"a.a.a.a", "Pod 1B", "NS 2", "C 4", "N", "N"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1B", "NS 2", "C 5", "Y", "N"}) + tw.AppendRow(Row{"b.b.b.b", "Pod 2", "NS 3", "C 6", "Y", "Y"}, rcAutoMerge) + tw.AppendRow(Row{"b.b.b.b", "Pod 2", "NS 3", "C 7", "Y", "Y"}, RowConfig{AutoMerge: true, AutoMergeAlign: text.AlignRight}) + tw.SetAutoIndex(true) + tw.SetColumnConfigs([]ColumnConfig{ + {Number: 5, Align: text.AlignCenter, AlignHeader: text.AlignCenter}, + {Number: 6, Align: text.AlignCenter, AlignHeader: text.AlignCenter}, + }) + tw.SetStyle(StyleLight) + tw.Style().Options.SeparateRows = true + + compareOutput(t, tw.Render(), ` +┌───┬─────────┬────────┬───────┬─────┬───┬───┐ +│ │ A │ B │ C │ D │ E │ F │ +├───┼─────────┼────────┼───────┼─────┼───┴───┤ +│ 1 │ a.a.a.a │ Pod 1A │ NS 1A │ C 1 │ Y │ +├───┼─────────┼────────┼───────┼─────┼───┬───┤ +│ 2 │ a.a.a.a │ Pod 1A │ NS 1A │ C 2 │ Y │ N │ +├───┼─────────┼────────┼───────┼─────┼───┼───┤ +│ 3 │ a.a.a.a │ Pod 1A │ NS 1B │ C 3 │ N │ N │ +├───┼─────────┼────────┼───────┼─────┼───┴───┤ +│ 4 │ a.a.a.a │ Pod 1B │ NS 2 │ C 4 │ N │ +├───┼─────────┼────────┼───────┼─────┼───┬───┤ +│ 5 │ a.a.a.a │ Pod 1B │ NS 2 │ C 5 │ Y │ N │ +├───┼─────────┼────────┼───────┼─────┼───┴───┤ +│ 6 │ b.b.b.b │ Pod 2 │ NS 3 │ C 6 │ Y │ +├───┼─────────┼────────┼───────┼─────┼───────┤ +│ 7 │ b.b.b.b │ Pod 2 │ NS 3 │ C 7 │ Y │ +└───┴─────────┴────────┴───────┴─────┴───────┘`) + }) + + t.Run("rows and columns and headers and footers", func(t *testing.T) { + tw := NewWriter() + tw.AppendHeader(Row{"Node IP", "Pods", "Namespace", "Container", "RCE", "RCE", "ID"}, rcAutoMerge) + tw.AppendHeader(Row{"Node IP", "Pods", "Namespace", "Container", "EXE", "RUN", ""}) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 1", "Y", "Y", 123}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 2", "Y", "N", 234}) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1B", "C 3", "N", "N", 345}) + tw.AppendRow(Row{"a.a.a.a", "Pod 1B", "NS 2", "C 4", "N", "N", 456}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1B", "NS 2", "C 5", "Y", "N", 567}) + tw.AppendRow(Row{"b.b.b.b", "Pod 2", "NS 3", "C 6", "Y", "Y", 678}, rcAutoMerge) + tw.AppendRow(Row{"b.b.b.b", "Pod 2", "NS 3", "C 7", "Y", "Y", 789}, rcAutoMerge) + tw.AppendFooter(Row{"", "", "", 7, 5, 5}, rcAutoMerge) + tw.AppendFooter(Row{"", "", "", 7, 5, 3}, rcAutoMerge) + tw.AppendFooter(Row{"", "", "", 7, 5, 5}, rcAutoMerge) + tw.AppendFooter(Row{"", "", "", 7, 5, 3}, rcAutoMerge) + tw.AppendFooter(Row{"", "", "", 7, 5, 5}, rcAutoMerge) + tw.SetAutoIndex(true) + tw.SetColumnConfigs([]ColumnConfig{ + {Number: 1, AutoMerge: true}, + {Number: 2, AutoMerge: true}, + {Number: 3, AutoMerge: true}, + {Number: 4, AutoMerge: true}, + {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter}, + {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter}, + }) + tw.SetStyle(StyleLight) + tw.Style().Options.SeparateRows = true + + compareOutput(t, tw.Render(), ` +┌───┬─────────┬────────┬───────────┬───────────┬───────────┬─────┐ +│ │ NODE IP │ PODS │ NAMESPACE │ CONTAINER │ RCE │ ID │ +│ │ │ │ │ ├─────┬─────┼─────┤ +│ │ │ │ │ │ EXE │ RUN │ │ +├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┼─────┤ +│ 1 │ a.a.a.a │ Pod 1A │ NS 1A │ C 1 │ Y │ 123 │ +├───┤ │ │ ├───────────┼─────┬─────┼─────┤ +│ 2 │ │ │ │ C 2 │ Y │ N │ 234 │ +├───┤ │ ├───────────┼───────────┼─────┼─────┼─────┤ +│ 3 │ │ │ NS 1B │ C 3 │ N │ N │ 345 │ +├───┤ ├────────┼───────────┼───────────┼─────┴─────┼─────┤ +│ 4 │ │ Pod 1B │ NS 2 │ C 4 │ N │ 456 │ +├───┤ │ │ ├───────────┼─────┬─────┼─────┤ +│ 5 │ │ │ │ C 5 │ Y │ N │ 567 │ +├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┼─────┤ +│ 6 │ b.b.b.b │ Pod 2 │ NS 3 │ C 6 │ Y │ 678 │ +├───┤ │ │ ├───────────┼───────────┼─────┤ +│ 7 │ │ │ │ C 7 │ Y │ 789 │ +├───┼─────────┴────────┴───────────┼───────────┼───────────┼─────┤ +│ │ │ 7 │ 5 │ │ +│ │ │ ├─────┬─────┼─────┤ +│ │ │ │ 5 │ 3 │ │ +│ │ │ ├─────┴─────┼─────┤ +│ │ │ │ 5 │ │ +│ │ │ ├─────┬─────┼─────┤ +│ │ │ │ 5 │ 3 │ │ +│ │ │ ├─────┴─────┼─────┤ +│ │ │ │ 5 │ │ +└───┴──────────────────────────────┴───────────┴───────────┴─────┘`) + }) + + t.Run("samurai sudoku", func(t *testing.T) { + tw := NewWriter() + tw.AppendRow(Row{"1.1\n1.1", "1.2\n1.2", "1.3\n1.3", " ", "2.1\n2.1", "2.2\n2.2", "2.3\n2.3"}) + tw.AppendRow(Row{"1.4\n1.4", "1.5\n1.5", "1.6\n1.6", " ", "2.4\n2.4", "2.5\n2.5", "2.6\n2.6"}) + tw.AppendRow(Row{"1.7\n1.7", "1.8\n1.8", "1.9\n0.1", "0.2\n0.2", "2.7\n0.3", "2.8\n2.8", "2.9\n2.9"}) + tw.AppendRow(Row{" ", " ", "0.4\n0.4", "0.5\n0.5", "0.6\n0.6", " ", " "}, rcAutoMerge) + tw.AppendRow(Row{"3.1\n3.1", "3.2\n3.2", "3.3\n0.7", "0.8\n0.8", "4.1\n0.9", "4.2\n4.2", "4.3\n4.3"}) + tw.AppendRow(Row{"3.4\n3.4", "3.5\n3.5", "3.6\n3.6", " ", "4.4\n4.4", "4.5\n4.5", "4.6\n4.6"}) + tw.AppendRow(Row{"3.7\n3.7", "3.8\n3.8", "3.9\n3.9", " ", "4.7\n4.7", "4.8\n4.8", "4.9\n4.9"}) + tw.SetColumnConfigs([]ColumnConfig{ + {Number: 4, AutoMerge: true}, + }) + tw.SetStyle(StyleLight) + tw.Style().Box.PaddingLeft = "" + tw.Style().Box.PaddingRight = "" + tw.Style().Options.DrawBorder = true + tw.Style().Options.SeparateRows = true + tw.Style().Options.SeparateColumns = true + + compareOutput(t, tw.Render(), ` +┌───┬───┬───┬───┬───┬───┬───┐ +│1.1│1.2│1.3│ │2.1│2.2│2.3│ +│1.1│1.2│1.3│ │2.1│2.2│2.3│ +├───┼───┼───┤ ├───┼───┼───┤ +│1.4│1.5│1.6│ │2.4│2.5│2.6│ +│1.4│1.5│1.6│ │2.4│2.5│2.6│ +├───┼───┼───┼───┼───┼───┼───┤ +│1.7│1.8│1.9│0.2│2.7│2.8│2.9│ +│1.7│1.8│0.1│0.2│0.3│2.8│2.9│ +├───┴───┼───┼───┼───┼───┴───┤ +│ │0.4│0.5│0.6│ │ +│ │0.4│0.5│0.6│ │ +├───┬───┼───┼───┼───┼───┬───┤ +│3.1│3.2│3.3│0.8│4.1│4.2│4.3│ +│3.1│3.2│0.7│0.8│0.9│4.2│4.3│ +├───┼───┼───┼───┼───┼───┼───┤ +│3.4│3.5│3.6│ │4.4│4.5│4.6│ +│3.4│3.5│3.6│ │4.4│4.5│4.6│ +├───┼───┼───┤ ├───┼───┼───┤ +│3.7│3.8│3.9│ │4.7│4.8│4.9│ +│3.7│3.8│3.9│ │4.7│4.8│4.9│ +└───┴───┴───┴───┴───┴───┴───┘`) + }) + + testLongCol1 := "4F8F5CB531E3D49A61CF417CD133792CCFA501FD8DA53EE368FED20E5FE0248C3A0B64F98A6533CEE1DA614C3A8DDEC791FF05FEE6D971D57C1348320F4EB42DR" + testLongRowCCs := []ColumnConfig{ + {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 24, WidthMaxEnforcer: text.WrapHard}, + {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 24, WidthMaxEnforcer: text.WrapHard}, + {Number: 7, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 24, WidthMaxEnforcer: text.WrapHard}, + {Number: 8, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 24, WidthMaxEnforcer: text.WrapHard}, + } + generateTableForLongRows := func() Writer { + tw := NewWriter() + tw.AppendHeader(Row{"Column 1", "Column 2", "Column 3", "Column 4", "Column 5", "Column 6", "Column 7", "Column 8"}, rcAutoMerge) + tw.SetAutoIndex(true) + tw.SetColumnConfigs(testLongRowCCs) + tw.SetStyle(StyleLight) + tw.Style().Options.SeparateRows = true + return tw + } + + t.Run("long column no merge", func(t *testing.T) { + tw := generateTableForLongRows() + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 1", testLongCol1, testLongCol1 + "W", testLongCol1 + "H", testLongCol1 + "Y"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 2", "Y", "Y", "Y", "Y"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 2", "Y", "Y", "Y", "Y"}, rcAutoMerge) + + compareOutput(t, tw.Render(), ` +┌───┬──────────┬──────────┬──────────┬──────────┬──────────────────────────┬──────────────────────────┬──────────────────────────┬──────────────────────────┐ +│ │ COLUMN 1 │ COLUMN 2 │ COLUMN 3 │ COLUMN 4 │ COLUMN 5 │ COLUMN 6 │ COLUMN 7 │ COLUMN 8 │ +├───┼──────────┼──────────┼──────────┼──────────┼──────────────────────────┼──────────────────────────┼──────────────────────────┼──────────────────────────┤ +│ 1 │ a.a.a.a │ Pod 1A │ NS 1A │ C 1 │ 4F8F5CB531E3D49A61CF417C │ 4F8F5CB531E3D49A61CF417C │ 4F8F5CB531E3D49A61CF417C │ 4F8F5CB531E3D49A61CF417C │ +│ │ │ │ │ │ D133792CCFA501FD8DA53EE3 │ D133792CCFA501FD8DA53EE3 │ D133792CCFA501FD8DA53EE3 │ D133792CCFA501FD8DA53EE3 │ +│ │ │ │ │ │ 68FED20E5FE0248C3A0B64F9 │ 68FED20E5FE0248C3A0B64F9 │ 68FED20E5FE0248C3A0B64F9 │ 68FED20E5FE0248C3A0B64F9 │ +│ │ │ │ │ │ 8A6533CEE1DA614C3A8DDEC7 │ 8A6533CEE1DA614C3A8DDEC7 │ 8A6533CEE1DA614C3A8DDEC7 │ 8A6533CEE1DA614C3A8DDEC7 │ +│ │ │ │ │ │ 91FF05FEE6D971D57C134832 │ 91FF05FEE6D971D57C134832 │ 91FF05FEE6D971D57C134832 │ 91FF05FEE6D971D57C134832 │ +│ │ │ │ │ │ 0F4EB42DR │ 0F4EB42DRW │ 0F4EB42DRH │ 0F4EB42DRY │ +├───┼──────────┼──────────┼──────────┼──────────┼──────────────────────────┴──────────────────────────┴──────────────────────────┴──────────────────────────┤ +│ 2 │ a.a.a.a │ Pod 1A │ NS 1A │ C 2 │ Y │ +├───┼──────────┼──────────┼──────────┼──────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│ 3 │ a.a.a.a │ Pod 1A │ NS 1A │ C 2 │ Y │ +└───┴──────────┴──────────┴──────────┴──────────┴───────────────────────────────────────────────────────────────────────────────────────────────────────────┘`) + }) + + t.Run("long column partially merged #1", func(t *testing.T) { + tw := generateTableForLongRows() + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 1", testLongCol1, testLongCol1, testLongCol1 + "R", testLongCol1 + "R"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 2", "Y", "Y", "Y", "Y"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 2", "Y", "Y", "Y", "Y"}, rcAutoMerge) + + compareOutput(t, tw.Render(), ` +┌───┬──────────┬──────────┬──────────┬──────────┬─────────────┬─────────────┬─────────────┬─────────────┐ +│ │ COLUMN 1 │ COLUMN 2 │ COLUMN 3 │ COLUMN 4 │ COLUMN 5 │ COLUMN 6 │ COLUMN 7 │ COLUMN 8 │ +├───┼──────────┼──────────┼──────────┼──────────┼─────────────┴─────────────┼─────────────┴─────────────┤ +│ 1 │ a.a.a.a │ Pod 1A │ NS 1A │ C 1 │ 4F8F5CB531E3D49A61CF417C │ 4F8F5CB531E3D49A61CF417C │ +│ │ │ │ │ │ D133792CCFA501FD8DA53EE3 │ D133792CCFA501FD8DA53EE3 │ +│ │ │ │ │ │ 68FED20E5FE0248C3A0B64F9 │ 68FED20E5FE0248C3A0B64F9 │ +│ │ │ │ │ │ 8A6533CEE1DA614C3A8DDEC7 │ 8A6533CEE1DA614C3A8DDEC7 │ +│ │ │ │ │ │ 91FF05FEE6D971D57C134832 │ 91FF05FEE6D971D57C134832 │ +│ │ │ │ │ │ 0F4EB42DR │ 0F4EB42DRR │ +├───┼──────────┼──────────┼──────────┼──────────┼───────────────────────────┴───────────────────────────┤ +│ 2 │ a.a.a.a │ Pod 1A │ NS 1A │ C 2 │ Y │ +├───┼──────────┼──────────┼──────────┼──────────┼───────────────────────────────────────────────────────┤ +│ 3 │ a.a.a.a │ Pod 1A │ NS 1A │ C 2 │ Y │ +└───┴──────────┴──────────┴──────────┴──────────┴───────────────────────────────────────────────────────┘`) + }) + + t.Run("long column partially merged #2", func(t *testing.T) { + tw := generateTableForLongRows() + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 1", testLongCol1, testLongCol1, testLongCol1, testLongCol1 + "E"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 2", "Y", "Y", "Y", "Y"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 2", "Y", "Y", "Y", "Y"}, rcAutoMerge) + + compareOutput(t, tw.Render(), ` +┌───┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────────────────────┐ +│ │ COLUMN 1 │ COLUMN 2 │ COLUMN 3 │ COLUMN 4 │ COLUMN 5 │ COLUMN 6 │ COLUMN 7 │ COLUMN 8 │ +├───┼──────────┼──────────┼──────────┼──────────┼──────────┴──────────┴──────────┼──────────────────────────┤ +│ 1 │ a.a.a.a │ Pod 1A │ NS 1A │ C 1 │ 4F8F5CB531E3D49A61CF417C │ 4F8F5CB531E3D49A61CF417C │ +│ │ │ │ │ │ D133792CCFA501FD8DA53EE3 │ D133792CCFA501FD8DA53EE3 │ +│ │ │ │ │ │ 68FED20E5FE0248C3A0B64F9 │ 68FED20E5FE0248C3A0B64F9 │ +│ │ │ │ │ │ 8A6533CEE1DA614C3A8DDEC7 │ 8A6533CEE1DA614C3A8DDEC7 │ +│ │ │ │ │ │ 91FF05FEE6D971D57C134832 │ 91FF05FEE6D971D57C134832 │ +│ │ │ │ │ │ 0F4EB42DR │ 0F4EB42DRE │ +├───┼──────────┼──────────┼──────────┼──────────┼────────────────────────────────┴──────────────────────────┤ +│ 2 │ a.a.a.a │ Pod 1A │ NS 1A │ C 2 │ Y │ +├───┼──────────┼──────────┼──────────┼──────────┼───────────────────────────────────────────────────────────┤ +│ 3 │ a.a.a.a │ Pod 1A │ NS 1A │ C 2 │ Y │ +└───┴──────────┴──────────┴──────────┴──────────┴───────────────────────────────────────────────────────────┘`) + }) + + t.Run("long column fully merged", func(t *testing.T) { + tw := generateTableForLongRows() + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 1", testLongCol1, testLongCol1, testLongCol1, testLongCol1}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 2", "Y", "Y", "Y", "Y"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 2", "Y", "Y", "Y", "Y"}, rcAutoMerge) + + compareOutput(t, tw.Render(), ` +┌───┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┐ +│ │ COLUMN 1 │ COLUMN 2 │ COLUMN 3 │ COLUMN 4 │ COLUMN 5 │ COLUMN 6 │ COLUMN 7 │ COLUMN 8 │ +├───┼──────────┼──────────┼──────────┼──────────┼──────────┴──────────┴──────────┴──────────┤ +│ 1 │ a.a.a.a │ Pod 1A │ NS 1A │ C 1 │ 4F8F5CB531E3D49A61CF417C │ +│ │ │ │ │ │ D133792CCFA501FD8DA53EE3 │ +│ │ │ │ │ │ 68FED20E5FE0248C3A0B64F9 │ +│ │ │ │ │ │ 8A6533CEE1DA614C3A8DDEC7 │ +│ │ │ │ │ │ 91FF05FEE6D971D57C134832 │ +│ │ │ │ │ │ 0F4EB42DR │ +├───┼──────────┼──────────┼──────────┼──────────┼───────────────────────────────────────────┤ +│ 2 │ a.a.a.a │ Pod 1A │ NS 1A │ C 2 │ Y │ +├───┼──────────┼──────────┼──────────┼──────────┼───────────────────────────────────────────┤ +│ 3 │ a.a.a.a │ Pod 1A │ NS 1A │ C 2 │ Y │ +└───┴──────────┴──────────┴──────────┴──────────┴───────────────────────────────────────────┘`) + }) + + t.Run("headers and footers", func(t *testing.T) { + tw := NewWriter() + tw.AppendHeader(Row{"Node IP", "Pods", "Namespace", "Container", "RCE1", "RCE2"}, rcAutoMerge) + tw.AppendHeader(Row{"", "", "", "", "EXE EXE EXE", "EXE EXE EXE"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 1", "Y", "Y"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 2", "Y", "Y"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1B", "C 3", "N", "N"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1B", "NS 2", "C 4", "N", "N"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1B", "NS 2", "C 5", "Y", "Y"}, rcAutoMerge) + tw.AppendRow(Row{"b.b.b.b", "Pod 2", "NS 3", "C 6", "Y", "Y"}, rcAutoMerge) + tw.AppendRow(Row{"b.b.b.b", "Pod 2", "NS 3", "C 7", "Y", "Y"}, rcAutoMerge) + tw.AppendFooter(Row{"", "", "", 7, 5, 5}, rcAutoMerge) + tw.AppendFooter(Row{"", "", "", 6, 4, 4}, rcAutoMerge) + tw.SetAutoIndex(true) + tw.SetColumnConfigs([]ColumnConfig{ + {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 7, WidthMaxEnforcer: text.WrapHard}, + {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 7, WidthMaxEnforcer: text.WrapHard}, + }) + tw.SetStyle(StyleLight) + tw.Style().Options.SeparateRows = true + + compareOutput(t, tw.Render(), ` +┌───┬─────────┬────────┬───────────┬───────────┬──────┬──────┐ +│ │ NODE IP │ PODS │ NAMESPACE │ CONTAINER │ RCE1 │ RCE2 │ +│ ├─────────┴────────┴───────────┴───────────┼──────┴──────┤ +│ │ │ EXE EXE │ +│ │ │ EXE │ +├───┼─────────┬────────┬───────────┬───────────┼─────────────┤ +│ 1 │ a.a.a.a │ Pod 1A │ NS 1A │ C 1 │ Y │ +├───┼─────────┼────────┼───────────┼───────────┼─────────────┤ +│ 2 │ a.a.a.a │ Pod 1A │ NS 1A │ C 2 │ Y │ +├───┼─────────┼────────┼───────────┼───────────┼─────────────┤ +│ 3 │ a.a.a.a │ Pod 1A │ NS 1B │ C 3 │ N │ +├───┼─────────┼────────┼───────────┼───────────┼─────────────┤ +│ 4 │ a.a.a.a │ Pod 1B │ NS 2 │ C 4 │ N │ +├───┼─────────┼────────┼───────────┼───────────┼─────────────┤ +│ 5 │ a.a.a.a │ Pod 1B │ NS 2 │ C 5 │ Y │ +├───┼─────────┼────────┼───────────┼───────────┼─────────────┤ +│ 6 │ b.b.b.b │ Pod 2 │ NS 3 │ C 6 │ Y │ +├───┼─────────┼────────┼───────────┼───────────┼─────────────┤ +│ 7 │ b.b.b.b │ Pod 2 │ NS 3 │ C 7 │ Y │ +├───┼─────────┴────────┴───────────┼───────────┼─────────────┤ +│ │ │ 7 │ 5 │ +│ ├──────────────────────────────┼───────────┼─────────────┤ +│ │ │ 6 │ 4 │ +└───┴──────────────────────────────┴───────────┴─────────────┘`) + }) + + t.Run("long header column", func(t *testing.T) { + tw := NewWriter() + tw.AppendHeader(Row{"Node IP", "Pods", "Namespace", "Container", "RCE1", "RCE2", "RCE3"}, rcAutoMerge) + tw.AppendHeader(Row{"", "", "", "", "EXE EXE EXE", "EXE EXE EXE", "EXE EXE EXE"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 1", "Y", "Y", "Y"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 2", "Y", "Y", "Y"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1B", "C 3", "N", "N", "N"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1B", "NS 2", "C 4", "N", "N", "N"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1B", "NS 2", "C 5", "Y", "Y", "Y"}, rcAutoMerge) + tw.AppendRow(Row{"b.b.b.b", "Pod 2", "NS 3", "C 6", "Y", "Y", "Y"}, rcAutoMerge) + tw.AppendRow(Row{"b.b.b.b", "Pod 2", "NS 3", "C 7", "Y", "Y", "Y"}, rcAutoMerge) + tw.AppendFooter(Row{"", "", "", 7, 5, 5, 5}, rcAutoMerge) + tw.AppendFooter(Row{"", "", "", 6, 4, 4, 3}, rcAutoMerge) + tw.SetAutoIndex(true) + tw.SetColumnConfigs([]ColumnConfig{ + {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 7, WidthMaxEnforcer: text.WrapHard}, + {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 7, WidthMaxEnforcer: text.WrapHard}, + {Number: 7, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 7, WidthMaxEnforcer: text.WrapHard}, + }) + tw.SetStyle(StyleLight) + tw.Style().Options.SeparateRows = true + + compareOutput(t, tw.Render(), ` +┌───┬─────────┬────────┬───────────┬───────────┬──────┬──────┬──────┐ +│ │ NODE IP │ PODS │ NAMESPACE │ CONTAINER │ RCE1 │ RCE2 │ RCE3 │ +│ ├─────────┴────────┴───────────┴───────────┼──────┴──────┴──────┤ +│ │ │ EXE EXE │ +│ │ │ EXE │ +├───┼─────────┬────────┬───────────┬───────────┼────────────────────┤ +│ 1 │ a.a.a.a │ Pod 1A │ NS 1A │ C 1 │ Y │ +├───┼─────────┼────────┼───────────┼───────────┼────────────────────┤ +│ 2 │ a.a.a.a │ Pod 1A │ NS 1A │ C 2 │ Y │ +├───┼─────────┼────────┼───────────┼───────────┼────────────────────┤ +│ 3 │ a.a.a.a │ Pod 1A │ NS 1B │ C 3 │ N │ +├───┼─────────┼────────┼───────────┼───────────┼────────────────────┤ +│ 4 │ a.a.a.a │ Pod 1B │ NS 2 │ C 4 │ N │ +├───┼─────────┼────────┼───────────┼───────────┼────────────────────┤ +│ 5 │ a.a.a.a │ Pod 1B │ NS 2 │ C 5 │ Y │ +├───┼─────────┼────────┼───────────┼───────────┼────────────────────┤ +│ 6 │ b.b.b.b │ Pod 2 │ NS 3 │ C 6 │ Y │ +├───┼─────────┼────────┼───────────┼───────────┼────────────────────┤ +│ 7 │ b.b.b.b │ Pod 2 │ NS 3 │ C 7 │ Y │ +├───┼─────────┴────────┴───────────┼───────────┼────────────────────┤ +│ │ │ 7 │ 5 │ +│ ├──────────────────────────────┼───────────┼─────────────┬──────┤ +│ │ │ 6 │ 4 │ 3 │ +└───┴──────────────────────────────┴───────────┴─────────────┴──────┘`) + }) + + t.Run("empty cells", func(t *testing.T) { + tw := NewWriter() + rowConfigAutoMerge := RowConfig{AutoMerge: true} + tw.AppendRow(Row{"Product", "Standalone", "foo bar", "a.a.a.a", ""}, rowConfigAutoMerge) + tw.AppendRow(Row{"Test", "Standalone", "bar baz", "b.b.b.b", ""}, rowConfigAutoMerge) + tw.AppendRow(Row{"Product", "RedisCluster", "foo baz", "", "Cluster #1"}, rowConfigAutoMerge) + tw.AppendRow(Row{"Product", "RedisCluster", "bar baz", "", "Cluster #2"}, rowConfigAutoMerge) + tw.SetAutoIndex(true) + tw.SetColumnConfigs([]ColumnConfig{ + {Number: 1, AutoMerge: true}, + {Number: 2, AutoMerge: true}, + {Number: 3, AutoMerge: true}, + {Number: 4, AutoMerge: true}, + {Number: 5, AutoMerge: true}, + }) + tw.SetStyle(StyleLight) + tw.Style().Options.SeparateRows = true + + compareOutput(t, tw.Render(), `┌───┬─────────┬──────────────┬─────────┬─────────┬────────────┐ +│ │ A │ B │ C │ D │ E │ +├───┼─────────┼──────────────┼─────────┼─────────┼────────────┤ +│ 1 │ Product │ Standalone │ foo bar │ a.a.a.a │ │ +├───┼─────────┤ ├─────────┼─────────┤ │ +│ 2 │ Test │ │ bar baz │ b.b.b.b │ │ +├───┼─────────┼──────────────┼─────────┼─────────┼────────────┤ +│ 3 │ Product │ RedisCluster │ foo baz │ │ Cluster #1 │ +├───┤ │ ├─────────┤ ├────────────┤ +│ 4 │ │ │ bar baz │ │ Cluster #2 │ +└───┴─────────┴──────────────┴─────────┴─────────┴────────────┘`) + }) + + t.Run("everything", func(t *testing.T) { + tw := NewWriter() + tw.AppendHeader(Row{"COLUMNS", "COLUMNS", "COLUMNS", "COLUMNS", "COLUMNS", "COLUMNS", "COLUMNS"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 1", "Y", "Y", "Y"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 2", "Y", "Y", "N"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1B", "C 3", "N", "N", "N"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1B", "NS 2", "C 4", "N", "Y", "N"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1B", "NS 2", "C 5", "Y", "Y", "Y"}, rcAutoMerge) + tw.AppendRow(Row{"b.b.b.b", "Pod 2", "NS 3", "C 6", "N", "Y", "Y"}, rcAutoMerge) + tw.AppendRow(Row{"b.b.b.b", "Pod 2", "NS 3", "C 7", "Y", "Y", "Y"}, rcAutoMerge) + tw.AppendFooter(Row{"foo", "foo", "foo", "foo", "bar", "bar", "bar"}, rcAutoMerge) + tw.AppendFooter(Row{7, 7, 7, 7, 7, 7, 7}, rcAutoMerge) + tw.SetAutoIndex(true) + tw.SetColumnConfigs([]ColumnConfig{ + {Number: 1, AutoMerge: true}, + {Number: 2, AutoMerge: true}, + {Number: 3, AutoMerge: true}, + {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 7, WidthMaxEnforcer: text.WrapHard}, + {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 7, WidthMaxEnforcer: text.WrapHard}, + {Number: 7, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 7, WidthMaxEnforcer: text.WrapHard}, + }) + tw.SetStyle(StyleLight) + tw.Style().Options.SeparateRows = true + + compareOutput(t, tw.Render(), ` +┌───┬───────────────────────────────────────────────────┐ +│ │ COLUMNS │ +├───┼─────────┬─────────┬─────────┬─────────┬───────────┤ +│ 1 │ a.a.a.a │ Pod 1A │ NS 1A │ C 1 │ Y │ +├───┤ │ │ ├─────────┼───────┬───┤ +│ 2 │ │ │ │ C 2 │ Y │ N │ +├───┤ │ ├─────────┼─────────┼───────┴───┤ +│ 3 │ │ │ NS 1B │ C 3 │ N │ +├───┤ ├─────────┼─────────┼─────────┼───┬───┬───┤ +│ 4 │ │ Pod 1B │ NS 2 │ C 4 │ N │ Y │ N │ +├───┤ │ │ ├─────────┼───┴───┴───┤ +│ 5 │ │ │ │ C 5 │ Y │ +├───┼─────────┼─────────┼─────────┼─────────┼───┬───────┤ +│ 6 │ b.b.b.b │ Pod 2 │ NS 3 │ C 6 │ N │ Y │ +├───┤ │ │ ├─────────┼───┴───────┤ +│ 7 │ │ │ │ C 7 │ Y │ +├───┼─────────┴─────────┴─────────┴─────────┼───────────┤ +│ │ FOO │ BAR │ +│ ├───────────────────────────────────────┴───────────┤ +│ │ 7 │ +└───┴───────────────────────────────────────────────────┘`) + }) +} + +func TestTable_Render_AutoMergeLongColumns(t *testing.T) { + rcAutoMerge := RowConfig{AutoMerge: true} + + testLongCol1 := "4F8F5CB531E3D49A61CF417CD133792CCFA501FD8DA53EE368FED20E5FE0248C3A0B64F98A6533CEE1DA614C3A8DDEC791FF05FEE6D971D57C1348320F4EB42DR" + testLongRowCCs := []ColumnConfig{ + {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 24, WidthMaxEnforcer: text.WrapHard}, + {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 24, WidthMaxEnforcer: text.WrapHard}, + {Number: 7, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 24, WidthMaxEnforcer: text.WrapHard}, + {Number: 8, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 24, WidthMaxEnforcer: text.WrapHard}, + } + generateTableForLongRows := func(longRow Row) Writer { + tw := NewWriter() + tw.AppendHeader(Row{"Column 1", "Column 2", "Column 3", "Column 4", "Column 5", "Column 6", "Column 7", "Column 8"}, rcAutoMerge) + tw.AppendRow(longRow, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 2", "Y", "Y", "Y", "Y"}, rcAutoMerge) + tw.AppendRow(Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 2", "Y", "Y", "Y", "Y"}, rcAutoMerge) + tw.SetAutoIndex(true) + tw.SetColumnConfigs(testLongRowCCs) + tw.SetStyle(StyleLight) + tw.Style().Options.SeparateRows = true + return tw + } + + t.Run("no merge", func(t *testing.T) { + tw := generateTableForLongRows( + Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 1", testLongCol1, testLongCol1 + "W", testLongCol1 + "H", testLongCol1 + "Y"}, + ) + + compareOutput(t, tw.Render(), ` +┌───┬──────────┬──────────┬──────────┬──────────┬──────────────────────────┬──────────────────────────┬──────────────────────────┬──────────────────────────┐ +│ │ COLUMN 1 │ COLUMN 2 │ COLUMN 3 │ COLUMN 4 │ COLUMN 5 │ COLUMN 6 │ COLUMN 7 │ COLUMN 8 │ +├───┼──────────┼──────────┼──────────┼──────────┼──────────────────────────┼──────────────────────────┼──────────────────────────┼──────────────────────────┤ +│ 1 │ a.a.a.a │ Pod 1A │ NS 1A │ C 1 │ 4F8F5CB531E3D49A61CF417C │ 4F8F5CB531E3D49A61CF417C │ 4F8F5CB531E3D49A61CF417C │ 4F8F5CB531E3D49A61CF417C │ +│ │ │ │ │ │ D133792CCFA501FD8DA53EE3 │ D133792CCFA501FD8DA53EE3 │ D133792CCFA501FD8DA53EE3 │ D133792CCFA501FD8DA53EE3 │ +│ │ │ │ │ │ 68FED20E5FE0248C3A0B64F9 │ 68FED20E5FE0248C3A0B64F9 │ 68FED20E5FE0248C3A0B64F9 │ 68FED20E5FE0248C3A0B64F9 │ +│ │ │ │ │ │ 8A6533CEE1DA614C3A8DDEC7 │ 8A6533CEE1DA614C3A8DDEC7 │ 8A6533CEE1DA614C3A8DDEC7 │ 8A6533CEE1DA614C3A8DDEC7 │ +│ │ │ │ │ │ 91FF05FEE6D971D57C134832 │ 91FF05FEE6D971D57C134832 │ 91FF05FEE6D971D57C134832 │ 91FF05FEE6D971D57C134832 │ +│ │ │ │ │ │ 0F4EB42DR │ 0F4EB42DRW │ 0F4EB42DRH │ 0F4EB42DRY │ +├───┼──────────┼──────────┼──────────┼──────────┼──────────────────────────┴──────────────────────────┴──────────────────────────┴──────────────────────────┤ +│ 2 │ a.a.a.a │ Pod 1A │ NS 1A │ C 2 │ Y │ +├───┼──────────┼──────────┼──────────┼──────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────┤ +│ 3 │ a.a.a.a │ Pod 1A │ NS 1A │ C 2 │ Y │ +└───┴──────────┴──────────┴──────────┴──────────┴───────────────────────────────────────────────────────────────────────────────────────────────────────────┘`) + }) + + t.Run("merge 2 pairs", func(t *testing.T) { + tw := generateTableForLongRows( + Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 1", testLongCol1, testLongCol1, testLongCol1 + "R", testLongCol1 + "R"}, + ) + + compareOutput(t, tw.Render(), ` +┌───┬──────────┬──────────┬──────────┬──────────┬─────────────┬─────────────┬─────────────┬─────────────┐ +│ │ COLUMN 1 │ COLUMN 2 │ COLUMN 3 │ COLUMN 4 │ COLUMN 5 │ COLUMN 6 │ COLUMN 7 │ COLUMN 8 │ +├───┼──────────┼──────────┼──────────┼──────────┼─────────────┴─────────────┼─────────────┴─────────────┤ +│ 1 │ a.a.a.a │ Pod 1A │ NS 1A │ C 1 │ 4F8F5CB531E3D49A61CF417C │ 4F8F5CB531E3D49A61CF417C │ +│ │ │ │ │ │ D133792CCFA501FD8DA53EE3 │ D133792CCFA501FD8DA53EE3 │ +│ │ │ │ │ │ 68FED20E5FE0248C3A0B64F9 │ 68FED20E5FE0248C3A0B64F9 │ +│ │ │ │ │ │ 8A6533CEE1DA614C3A8DDEC7 │ 8A6533CEE1DA614C3A8DDEC7 │ +│ │ │ │ │ │ 91FF05FEE6D971D57C134832 │ 91FF05FEE6D971D57C134832 │ +│ │ │ │ │ │ 0F4EB42DR │ 0F4EB42DRR │ +├───┼──────────┼──────────┼──────────┼──────────┼───────────────────────────┴───────────────────────────┤ +│ 2 │ a.a.a.a │ Pod 1A │ NS 1A │ C 2 │ Y │ +├───┼──────────┼──────────┼──────────┼──────────┼───────────────────────────────────────────────────────┤ +│ 3 │ a.a.a.a │ Pod 1A │ NS 1A │ C 2 │ Y │ +└───┴──────────┴──────────┴──────────┴──────────┴───────────────────────────────────────────────────────┘`) + }) + + t.Run("marge 3 columns", func(t *testing.T) { + tw := generateTableForLongRows( + Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 1", testLongCol1, testLongCol1, testLongCol1, testLongCol1 + "E"}, + ) + + compareOutput(t, tw.Render(), ` +┌───┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────────────────────┐ +│ │ COLUMN 1 │ COLUMN 2 │ COLUMN 3 │ COLUMN 4 │ COLUMN 5 │ COLUMN 6 │ COLUMN 7 │ COLUMN 8 │ +├───┼──────────┼──────────┼──────────┼──────────┼──────────┴──────────┴──────────┼──────────────────────────┤ +│ 1 │ a.a.a.a │ Pod 1A │ NS 1A │ C 1 │ 4F8F5CB531E3D49A61CF417C │ 4F8F5CB531E3D49A61CF417C │ +│ │ │ │ │ │ D133792CCFA501FD8DA53EE3 │ D133792CCFA501FD8DA53EE3 │ +│ │ │ │ │ │ 68FED20E5FE0248C3A0B64F9 │ 68FED20E5FE0248C3A0B64F9 │ +│ │ │ │ │ │ 8A6533CEE1DA614C3A8DDEC7 │ 8A6533CEE1DA614C3A8DDEC7 │ +│ │ │ │ │ │ 91FF05FEE6D971D57C134832 │ 91FF05FEE6D971D57C134832 │ +│ │ │ │ │ │ 0F4EB42DR │ 0F4EB42DRE │ +├───┼──────────┼──────────┼──────────┼──────────┼────────────────────────────────┴──────────────────────────┤ +│ 2 │ a.a.a.a │ Pod 1A │ NS 1A │ C 2 │ Y │ +├───┼──────────┼──────────┼──────────┼──────────┼───────────────────────────────────────────────────────────┤ +│ 3 │ a.a.a.a │ Pod 1A │ NS 1A │ C 2 │ Y │ +└───┴──────────┴──────────┴──────────┴──────────┴───────────────────────────────────────────────────────────┘`) + }) + + t.Run("merge 4 columns", func(t *testing.T) { + tw := generateTableForLongRows( + Row{"a.a.a.a", "Pod 1A", "NS 1A", "C 1", testLongCol1, testLongCol1, testLongCol1, testLongCol1}, + ) + + compareOutput(t, tw.Render(), ` +┌───┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┐ +│ │ COLUMN 1 │ COLUMN 2 │ COLUMN 3 │ COLUMN 4 │ COLUMN 5 │ COLUMN 6 │ COLUMN 7 │ COLUMN 8 │ +├───┼──────────┼──────────┼──────────┼──────────┼──────────┴──────────┴──────────┴──────────┤ +│ 1 │ a.a.a.a │ Pod 1A │ NS 1A │ C 1 │ 4F8F5CB531E3D49A61CF417C │ +│ │ │ │ │ │ D133792CCFA501FD8DA53EE3 │ +│ │ │ │ │ │ 68FED20E5FE0248C3A0B64F9 │ +│ │ │ │ │ │ 8A6533CEE1DA614C3A8DDEC7 │ +│ │ │ │ │ │ 91FF05FEE6D971D57C134832 │ +│ │ │ │ │ │ 0F4EB42DR │ +├───┼──────────┼──────────┼──────────┼──────────┼───────────────────────────────────────────┤ +│ 2 │ a.a.a.a │ Pod 1A │ NS 1A │ C 2 │ Y │ +├───┼──────────┼──────────┼──────────┼──────────┼───────────────────────────────────────────┤ +│ 3 │ a.a.a.a │ Pod 1A │ NS 1A │ C 2 │ Y │ +└───┴──────────┴──────────┴──────────┴──────────┴───────────────────────────────────────────┘`) + }) +} diff --git a/table/render_csv.go b/table/render_csv.go index 07da673..831194e 100644 --- a/table/render_csv.go +++ b/table/render_csv.go @@ -7,11 +7,12 @@ import ( ) // RenderCSV renders the Table in CSV format. Example: -// #,First Name,Last Name,Salary, -// 1,Arya,Stark,3000, -// 20,Jon,Snow,2000,"You know nothing\, Jon Snow!" -// 300,Tyrion,Lannister,5000, -// ,,Total,10000, +// +// #,First Name,Last Name,Salary, +// 1,Arya,Stark,3000, +// 20,Jon,Snow,2000,"You know nothing\, Jon Snow!" +// 300,Tyrion,Lannister,5000, +// ,,Total,10000, func (t *Table) RenderCSV() string { t.initForRender() diff --git a/table/render_html.go b/table/render_html.go index bf712ec..fcea86b 100644 --- a/table/render_html.go +++ b/table/render_html.go @@ -13,49 +13,50 @@ const ( ) // RenderHTML renders the Table in HTML format. Example: -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -//
#First NameLast NameSalary 
1AryaStark3000 
20JonSnow2000You know nothing, Jon Snow!
300TyrionLannister5000 
  Total10000 
+// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +//
#First NameLast NameSalary 
1AryaStark3000 
20JonSnow2000You know nothing, Jon Snow!
300TyrionLannister5000 
  Total10000 
func (t *Table) RenderHTML() string { t.initForRender() diff --git a/table/render_markdown.go b/table/render_markdown.go index 1a8c488..adf573f 100644 --- a/table/render_markdown.go +++ b/table/render_markdown.go @@ -6,12 +6,13 @@ import ( ) // RenderMarkdown renders the Table in Markdown format. Example: -// | # | First Name | Last Name | Salary | | -// | ---:| --- | --- | ---:| --- | -// | 1 | Arya | Stark | 3000 | | -// | 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! | -// | 300 | Tyrion | Lannister | 5000 | | -// | | | Total | 10000 | | +// +// | # | First Name | Last Name | Salary | | +// | ---:| --- | --- | ---:| --- | +// | 1 | Arya | Stark | 3000 | | +// | 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! | +// | 300 | Tyrion | Lannister | 5000 | | +// | | | Total | 10000 | | func (t *Table) RenderMarkdown() string { t.initForRender() diff --git a/table/render_test.go b/table/render_test.go index abeaa79..abc1a72 100644 --- a/table/render_test.go +++ b/table/render_test.go @@ -140,692 +140,6 @@ func TestTable_Render_AutoIndex(t *testing.T) { └────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘`) } -func TestTable_Render_AutoMerge(t *testing.T) { - rcAutoMerge := RowConfig{AutoMerge: true} - - t.Run("columns only", func(t *testing.T) { - tw := NewWriter() - tw.AppendHeader(Row{"Node IP", "Pods", "Namespace", "Container", "RCE\nEXE", "RCE\nRUN"}) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 1", "Y", "Y"}) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "N"}) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1B", "C 3", "N", "N"}) - tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 4", "N", "N"}) - tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 5", "Y", "N"}) - tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 6", "Y", "Y"}) - tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 7", "Y", "Y"}) - tw.AppendFooter(Row{"", "", "", 7, 5, 3}) - tw.SetAutoIndex(true) - tw.SetColumnConfigs([]ColumnConfig{ - {Number: 1, AutoMerge: true}, - {Number: 2, AutoMerge: true}, - {Number: 3, AutoMerge: true}, - {Number: 4, AutoMerge: true}, - {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter}, - {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter}, - }) - tw.SetStyle(StyleLight) - tw.Style().Options.SeparateRows = true - - compareOutput(t, tw.Render(), ` -┌───┬─────────┬────────┬───────────┬───────────┬─────┬─────┐ -│ │ NODE IP │ PODS │ NAMESPACE │ CONTAINER │ RCE │ RCE │ -│ │ │ │ │ │ EXE │ RUN │ -├───┼─────────┼────────┼───────────┼───────────┼─────┼─────┤ -│ 1 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 1 │ Y │ Y │ -├───┤ │ │ ├───────────┼─────┼─────┤ -│ 2 │ │ │ │ C 2 │ Y │ N │ -├───┤ │ ├───────────┼───────────┼─────┼─────┤ -│ 3 │ │ │ NS 1B │ C 3 │ N │ N │ -├───┤ ├────────┼───────────┼───────────┼─────┼─────┤ -│ 4 │ │ Pod 1B │ NS 2 │ C 4 │ N │ N │ -├───┤ │ │ ├───────────┼─────┼─────┤ -│ 5 │ │ │ │ C 5 │ Y │ N │ -├───┼─────────┼────────┼───────────┼───────────┼─────┼─────┤ -│ 6 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 6 │ Y │ Y │ -├───┤ │ │ ├───────────┼─────┼─────┤ -│ 7 │ │ │ │ C 7 │ Y │ Y │ -├───┼─────────┼────────┼───────────┼───────────┼─────┼─────┤ -│ │ │ │ │ 7 │ 5 │ 3 │ -└───┴─────────┴────────┴───────────┴───────────┴─────┴─────┘`) - }) - - t.Run("columns only with hidden columns", func(t *testing.T) { - tw := NewWriter() - tw.AppendHeader(Row{"Node IP", "Pods", "Namespace", "Container", "RCE\nEXE", "RCE\nRUN"}) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 1", "Y", "Y"}) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "N"}) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1B", "C 3", "N", "N"}) - tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 4", "Y", "Y"}) - tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 5", "Y", "N"}) - tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 6", "Y", "Y"}) - tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 7", "Y", "N"}) - tw.AppendFooter(Row{"", "", "", 7, 5, 3}) - tw.SetColumnConfigs([]ColumnConfig{ - {Number: 1, AutoMerge: true}, - {Number: 2, AutoMerge: true}, - {Number: 3, AutoMerge: true}, - {Number: 4, Hidden: true}, - {Number: 5, Hidden: true, Align: text.AlignCenter}, - {Number: 6, Hidden: true, Align: text.AlignCenter}, - }) - tw.SetStyle(StyleLight) - tw.Style().Options.SeparateRows = true - - compareOutput(t, tw.Render(), ` -┌─────────┬────────┬───────────┐ -│ NODE IP │ PODS │ NAMESPACE │ -├─────────┼────────┼───────────┤ -│ 1.1.1.1 │ Pod 1A │ NS 1A │ -│ │ │ │ -│ │ │ │ -│ │ ├───────────┤ -│ │ │ NS 1B │ -│ ├────────┼───────────┤ -│ │ Pod 1B │ NS 2 │ -│ │ │ │ -│ │ │ │ -├─────────┼────────┼───────────┤ -│ 2.2.2.2 │ Pod 2 │ NS 3 │ -│ │ │ │ -│ │ │ │ -├─────────┼────────┼───────────┤ -│ │ │ │ -└─────────┴────────┴───────────┘`) - }) - - t.Run("rows only", func(t *testing.T) { - tw := NewWriter() - tw.AppendHeader(Row{"Node IP", "Pods", "Namespace", "Container", "RCE", "RCE"}, rcAutoMerge) - tw.AppendHeader(Row{"", "", "", "", "EXE", "RUN"}) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 1", "Y", "Y"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "N"}) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1B", "C 3", "N", "N"}) - tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 4", "N", "N"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 5", "Y", "N"}) - tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 6", "Y", "Y"}, rcAutoMerge) - tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 7", "Y", "Y"}, RowConfig{AutoMerge: true, AutoMergeAlign: text.AlignRight}) - tw.AppendFooter(Row{"", "", "", 7, 5, 3}) - tw.SetAutoIndex(true) - tw.SetColumnConfigs([]ColumnConfig{ - {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter}, - {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter}, - }) - tw.SetStyle(StyleLight) - tw.Style().Options.SeparateRows = true - - compareOutput(t, tw.Render(), ` -┌───┬─────────┬────────┬───────────┬───────────┬───────────┐ -│ │ NODE IP │ PODS │ NAMESPACE │ CONTAINER │ RCE │ -│ ├─────────┼────────┼───────────┼───────────┼─────┬─────┤ -│ │ │ │ │ │ EXE │ RUN │ -├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┤ -│ 1 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 1 │ Y │ -├───┼─────────┼────────┼───────────┼───────────┼─────┬─────┤ -│ 2 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 2 │ Y │ N │ -├───┼─────────┼────────┼───────────┼───────────┼─────┼─────┤ -│ 3 │ 1.1.1.1 │ Pod 1A │ NS 1B │ C 3 │ N │ N │ -├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┤ -│ 4 │ 1.1.1.1 │ Pod 1B │ NS 2 │ C 4 │ N │ -├───┼─────────┼────────┼───────────┼───────────┼─────┬─────┤ -│ 5 │ 1.1.1.1 │ Pod 1B │ NS 2 │ C 5 │ Y │ N │ -├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┤ -│ 6 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 6 │ Y │ -├───┼─────────┼────────┼───────────┼───────────┼───────────┤ -│ 7 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 7 │ Y │ -├───┼─────────┼────────┼───────────┼───────────┼─────┬─────┤ -│ │ │ │ │ 7 │ 5 │ 3 │ -└───┴─────────┴────────┴───────────┴───────────┴─────┴─────┘`) - }) - - t.Run("rows and columns", func(t *testing.T) { - tw := NewWriter() - tw.AppendHeader(Row{"Node IP", "Pods", "Namespace", "Container", "RCE", "RCE"}, rcAutoMerge) - tw.AppendHeader(Row{"", "", "", "", "EXE", "RUN"}) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 1", "Y", "Y"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "N"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1B", "C 3", "N", "N"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 4", "N", "N"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 5", "Y", "N"}, rcAutoMerge) - tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 6", "Y", "Y"}, rcAutoMerge) - tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 7", "Y", "Y"}, rcAutoMerge) - tw.AppendFooter(Row{"", "", "", 7, 5, 3}) - tw.SetAutoIndex(true) - tw.SetColumnConfigs([]ColumnConfig{ - {Number: 1, AutoMerge: true}, - {Number: 2, AutoMerge: true}, - {Number: 3, AutoMerge: true}, - {Number: 4, AutoMerge: true}, - {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter}, - {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter}, - }) - tw.SetStyle(StyleLight) - tw.Style().Options.SeparateRows = true - - compareOutput(t, tw.Render(), ` -┌───┬─────────┬────────┬───────────┬───────────┬───────────┐ -│ │ NODE IP │ PODS │ NAMESPACE │ CONTAINER │ RCE │ -│ │ │ │ │ ├─────┬─────┤ -│ │ │ │ │ │ EXE │ RUN │ -├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┤ -│ 1 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 1 │ Y │ -├───┤ │ │ ├───────────┼─────┬─────┤ -│ 2 │ │ │ │ C 2 │ Y │ N │ -├───┤ │ ├───────────┼───────────┼─────┴─────┤ -│ 3 │ │ │ NS 1B │ C 3 │ N │ -├───┤ ├────────┼───────────┼───────────┼───────────┤ -│ 4 │ │ Pod 1B │ NS 2 │ C 4 │ N │ -├───┤ │ │ ├───────────┼─────┬─────┤ -│ 5 │ │ │ │ C 5 │ Y │ N │ -├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┤ -│ 6 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 6 │ Y │ -├───┤ │ │ ├───────────┼───────────┤ -│ 7 │ │ │ │ C 7 │ Y │ -├───┼─────────┼────────┼───────────┼───────────┼─────┬─────┤ -│ │ │ │ │ 7 │ 5 │ 3 │ -└───┴─────────┴────────┴───────────┴───────────┴─────┴─────┘`) - }) - - t.Run("rows and columns no headers or footers", func(t *testing.T) { - tw := NewWriter() - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 1", "Y", "Y"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "N"}) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1B", "C 3", "N", "N"}) - tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 4", "N", "N"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 5", "Y", "N"}) - tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 6", "Y", "Y"}, rcAutoMerge) - tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 7", "Y", "Y"}, RowConfig{AutoMerge: true, AutoMergeAlign: text.AlignRight}) - tw.SetColumnConfigs([]ColumnConfig{ - {Number: 5, Align: text.AlignCenter, AlignHeader: text.AlignCenter}, - {Number: 6, Align: text.AlignCenter, AlignHeader: text.AlignCenter}, - }) - tw.SetStyle(StyleLight) - tw.Style().Options.SeparateRows = true - - compareOutput(t, tw.Render(), ` -┌─────────┬────────┬───────┬─────┬───────┐ -│ 1.1.1.1 │ Pod 1A │ NS 1A │ C 1 │ Y │ -├─────────┼────────┼───────┼─────┼───┬───┤ -│ 1.1.1.1 │ Pod 1A │ NS 1A │ C 2 │ Y │ N │ -├─────────┼────────┼───────┼─────┼───┼───┤ -│ 1.1.1.1 │ Pod 1A │ NS 1B │ C 3 │ N │ N │ -├─────────┼────────┼───────┼─────┼───┴───┤ -│ 1.1.1.1 │ Pod 1B │ NS 2 │ C 4 │ N │ -├─────────┼────────┼───────┼─────┼───┬───┤ -│ 1.1.1.1 │ Pod 1B │ NS 2 │ C 5 │ Y │ N │ -├─────────┼────────┼───────┼─────┼───┴───┤ -│ 2.2.2.2 │ Pod 2 │ NS 3 │ C 6 │ Y │ -├─────────┼────────┼───────┼─────┼───────┤ -│ 2.2.2.2 │ Pod 2 │ NS 3 │ C 7 │ Y │ -└─────────┴────────┴───────┴─────┴───────┘`) - }) - - t.Run("rows and columns no headers or footers with auto-index", func(t *testing.T) { - tw := NewWriter() - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 1", "Y", "Y"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "N"}) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1B", "C 3", "N", "N"}) - tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 4", "N", "N"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 5", "Y", "N"}) - tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 6", "Y", "Y"}, rcAutoMerge) - tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 7", "Y", "Y"}, RowConfig{AutoMerge: true, AutoMergeAlign: text.AlignRight}) - tw.SetAutoIndex(true) - tw.SetColumnConfigs([]ColumnConfig{ - {Number: 5, Align: text.AlignCenter, AlignHeader: text.AlignCenter}, - {Number: 6, Align: text.AlignCenter, AlignHeader: text.AlignCenter}, - }) - tw.SetStyle(StyleLight) - tw.Style().Options.SeparateRows = true - - compareOutput(t, tw.Render(), ` -┌───┬─────────┬────────┬───────┬─────┬───┬───┐ -│ │ A │ B │ C │ D │ E │ F │ -├───┼─────────┼────────┼───────┼─────┼───┴───┤ -│ 1 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 1 │ Y │ -├───┼─────────┼────────┼───────┼─────┼───┬───┤ -│ 2 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 2 │ Y │ N │ -├───┼─────────┼────────┼───────┼─────┼───┼───┤ -│ 3 │ 1.1.1.1 │ Pod 1A │ NS 1B │ C 3 │ N │ N │ -├───┼─────────┼────────┼───────┼─────┼───┴───┤ -│ 4 │ 1.1.1.1 │ Pod 1B │ NS 2 │ C 4 │ N │ -├───┼─────────┼────────┼───────┼─────┼───┬───┤ -│ 5 │ 1.1.1.1 │ Pod 1B │ NS 2 │ C 5 │ Y │ N │ -├───┼─────────┼────────┼───────┼─────┼───┴───┤ -│ 6 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 6 │ Y │ -├───┼─────────┼────────┼───────┼─────┼───────┤ -│ 7 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 7 │ Y │ -└───┴─────────┴────────┴───────┴─────┴───────┘`) - }) - - t.Run("rows and columns and footers", func(t *testing.T) { - tw := NewWriter() - tw.AppendHeader(Row{"Node IP", "Pods", "Namespace", "Container", "RCE", "RCE", "ID"}, rcAutoMerge) - tw.AppendHeader(Row{"", "", "", "", "EXE", "RUN", ""}) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 1", "Y", "Y", 123}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "N", 234}) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1B", "C 3", "N", "N", 345}) - tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 4", "N", "N", 456}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 5", "Y", "N", 567}) - tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 6", "Y", "Y", 678}, rcAutoMerge) - tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 7", "Y", "Y", 789}, rcAutoMerge) - tw.AppendFooter(Row{"", "", "", 7, 5, 5}, rcAutoMerge) - tw.AppendFooter(Row{"", "", "", 7, 5, 3}, rcAutoMerge) - tw.AppendFooter(Row{"", "", "", 7, 5, 5}, rcAutoMerge) - tw.AppendFooter(Row{"", "", "", 7, 5, 3}, rcAutoMerge) - tw.AppendFooter(Row{"", "", "", 7, 5, 5}, rcAutoMerge) - tw.SetAutoIndex(true) - tw.SetColumnConfigs([]ColumnConfig{ - {Number: 1, AutoMerge: true}, - {Number: 2, AutoMerge: true}, - {Number: 3, AutoMerge: true}, - {Number: 4, AutoMerge: true}, - {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter}, - {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter}, - }) - tw.SetStyle(StyleLight) - tw.Style().Options.SeparateRows = true - - compareOutput(t, tw.Render(), ` -┌───┬─────────┬────────┬───────────┬───────────┬───────────┬─────┐ -│ │ NODE IP │ PODS │ NAMESPACE │ CONTAINER │ RCE │ ID │ -│ │ │ │ │ ├─────┬─────┼─────┤ -│ │ │ │ │ │ EXE │ RUN │ │ -├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┼─────┤ -│ 1 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 1 │ Y │ 123 │ -├───┤ │ │ ├───────────┼─────┬─────┼─────┤ -│ 2 │ │ │ │ C 2 │ Y │ N │ 234 │ -├───┤ │ ├───────────┼───────────┼─────┼─────┼─────┤ -│ 3 │ │ │ NS 1B │ C 3 │ N │ N │ 345 │ -├───┤ ├────────┼───────────┼───────────┼─────┴─────┼─────┤ -│ 4 │ │ Pod 1B │ NS 2 │ C 4 │ N │ 456 │ -├───┤ │ │ ├───────────┼─────┬─────┼─────┤ -│ 5 │ │ │ │ C 5 │ Y │ N │ 567 │ -├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┼─────┤ -│ 6 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 6 │ Y │ 678 │ -├───┤ │ │ ├───────────┼───────────┼─────┤ -│ 7 │ │ │ │ C 7 │ Y │ 789 │ -├───┼─────────┴────────┴───────────┼───────────┼───────────┼─────┤ -│ │ │ 7 │ 5 │ │ -│ │ │ ├─────┬─────┼─────┤ -│ │ │ │ 5 │ 3 │ │ -│ │ │ ├─────┴─────┼─────┤ -│ │ │ │ 5 │ │ -│ │ │ ├─────┬─────┼─────┤ -│ │ │ │ 5 │ 3 │ │ -│ │ │ ├─────┴─────┼─────┤ -│ │ │ │ 5 │ │ -└───┴──────────────────────────────┴───────────┴───────────┴─────┘`) - }) - - t.Run("samurai sudoku", func(t *testing.T) { - tw := NewWriter() - tw.AppendRow(Row{"1.1\n1.1", "1.2\n1.2", "1.3\n1.3", " ", "2.1\n2.1", "2.2\n2.2", "2.3\n2.3"}) - tw.AppendRow(Row{"1.4\n1.4", "1.5\n1.5", "1.6\n1.6", " ", "2.4\n2.4", "2.5\n2.5", "2.6\n2.6"}) - tw.AppendRow(Row{"1.7\n1.7", "1.8\n1.8", "1.9\n0.1", "0.2\n0.2", "2.7\n0.3", "2.8\n2.8", "2.9\n2.9"}) - tw.AppendRow(Row{" ", " ", "0.4\n0.4", "0.5\n0.5", "0.6\n0.6", " ", " "}, rcAutoMerge) - tw.AppendRow(Row{"3.1\n3.1", "3.2\n3.2", "3.3\n0.7", "0.8\n0.8", "4.1\n0.9", "4.2\n4.2", "4.3\n4.3"}) - tw.AppendRow(Row{"3.4\n3.4", "3.5\n3.5", "3.6\n3.6", " ", "4.4\n4.4", "4.5\n4.5", "4.6\n4.6"}) - tw.AppendRow(Row{"3.7\n3.7", "3.8\n3.8", "3.9\n3.9", " ", "4.7\n4.7", "4.8\n4.8", "4.9\n4.9"}) - tw.SetColumnConfigs([]ColumnConfig{ - {Number: 4, AutoMerge: true}, - }) - tw.SetStyle(StyleLight) - tw.Style().Box.PaddingLeft = "" - tw.Style().Box.PaddingRight = "" - tw.Style().Options.DrawBorder = true - tw.Style().Options.SeparateRows = true - tw.Style().Options.SeparateColumns = true - - compareOutput(t, tw.Render(), ` -┌───┬───┬───┬───┬───┬───┬───┐ -│1.1│1.2│1.3│ │2.1│2.2│2.3│ -│1.1│1.2│1.3│ │2.1│2.2│2.3│ -├───┼───┼───┤ ├───┼───┼───┤ -│1.4│1.5│1.6│ │2.4│2.5│2.6│ -│1.4│1.5│1.6│ │2.4│2.5│2.6│ -├───┼───┼───┼───┼───┼───┼───┤ -│1.7│1.8│1.9│0.2│2.7│2.8│2.9│ -│1.7│1.8│0.1│0.2│0.3│2.8│2.9│ -├───┴───┼───┼───┼───┼───┴───┤ -│ │0.4│0.5│0.6│ │ -│ │0.4│0.5│0.6│ │ -├───┬───┼───┼───┼───┼───┬───┤ -│3.1│3.2│3.3│0.8│4.1│4.2│4.3│ -│3.1│3.2│0.7│0.8│0.9│4.2│4.3│ -├───┼───┼───┼───┼───┼───┼───┤ -│3.4│3.5│3.6│ │4.4│4.5│4.6│ -│3.4│3.5│3.6│ │4.4│4.5│4.6│ -├───┼───┼───┤ ├───┼───┼───┤ -│3.7│3.8│3.9│ │4.7│4.8│4.9│ -│3.7│3.8│3.9│ │4.7│4.8│4.9│ -└───┴───┴───┴───┴───┴───┴───┘`) - }) - - t.Run("long column no merge", func(t *testing.T) { - tw := NewWriter() - tw.AppendHeader(Row{"Column 1", "Column 2", "Column 3", "Column 4", "Column 5", "Column 6", "Column 7", "Column 8"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 1", "4F8F5CB531E3D49A61CF417CD133792CCFA501FD8DA53EE368FED20E5FE0248C3A0B64F98A6533CEE1DA614C3A8DDEC791FF05FEE6D971D57C1348320F4EB42DR", "4F8F5CB531E3D49A61CF417CD133792CCFA501FD8DA53EE368FED20E5FE0248C3A0B64F98A6533CEE1DA614C3A8DDEC791FF05FEE6D971D57C1348320F4EB42DRW", "4F8F5CB531E3D49A61CF417CD133792CCFA501FD8DA53EE368FED20E5FE0248C3A0B64F98A6533CEE1DA614C3A8DDEC791FF05FEE6D971D57C1348320F4EB42DRH", "4F8F5CB531E3D49A61CF417CD133792CCFA501FD8DA53EE368FED20E5FE0248C3A0B64F98A6533CEE1DA614C3A8DDEC791FF05FEE6D971D57C1348320F4EB42DRY"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "Y", "Y", "Y"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "Y", "Y", "Y"}, rcAutoMerge) - tw.SetAutoIndex(true) - tw.SetColumnConfigs([]ColumnConfig{ - {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 24, WidthMaxEnforcer: text.WrapHard}, - {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 24, WidthMaxEnforcer: text.WrapHard}, - {Number: 7, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 24, WidthMaxEnforcer: text.WrapHard}, - {Number: 8, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 24, WidthMaxEnforcer: text.WrapHard}, - }) - tw.SetStyle(StyleLight) - tw.Style().Options.SeparateRows = true - - compareOutput(t, tw.Render(), ` -┌───┬──────────┬──────────┬──────────┬──────────┬──────────────────────────┬──────────────────────────┬──────────────────────────┬──────────────────────────┐ -│ │ COLUMN 1 │ COLUMN 2 │ COLUMN 3 │ COLUMN 4 │ COLUMN 5 │ COLUMN 6 │ COLUMN 7 │ COLUMN 8 │ -├───┼──────────┼──────────┼──────────┼──────────┼──────────────────────────┼──────────────────────────┼──────────────────────────┼──────────────────────────┤ -│ 1 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 1 │ 4F8F5CB531E3D49A61CF417C │ 4F8F5CB531E3D49A61CF417C │ 4F8F5CB531E3D49A61CF417C │ 4F8F5CB531E3D49A61CF417C │ -│ │ │ │ │ │ D133792CCFA501FD8DA53EE3 │ D133792CCFA501FD8DA53EE3 │ D133792CCFA501FD8DA53EE3 │ D133792CCFA501FD8DA53EE3 │ -│ │ │ │ │ │ 68FED20E5FE0248C3A0B64F9 │ 68FED20E5FE0248C3A0B64F9 │ 68FED20E5FE0248C3A0B64F9 │ 68FED20E5FE0248C3A0B64F9 │ -│ │ │ │ │ │ 8A6533CEE1DA614C3A8DDEC7 │ 8A6533CEE1DA614C3A8DDEC7 │ 8A6533CEE1DA614C3A8DDEC7 │ 8A6533CEE1DA614C3A8DDEC7 │ -│ │ │ │ │ │ 91FF05FEE6D971D57C134832 │ 91FF05FEE6D971D57C134832 │ 91FF05FEE6D971D57C134832 │ 91FF05FEE6D971D57C134832 │ -│ │ │ │ │ │ 0F4EB42DR │ 0F4EB42DRW │ 0F4EB42DRH │ 0F4EB42DRY │ -├───┼──────────┼──────────┼──────────┼──────────┼──────────────────────────┴──────────────────────────┴──────────────────────────┴──────────────────────────┤ -│ 2 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 2 │ Y │ -├───┼──────────┼──────────┼──────────┼──────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────┤ -│ 3 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 2 │ Y │ -└───┴──────────┴──────────┴──────────┴──────────┴───────────────────────────────────────────────────────────────────────────────────────────────────────────┘`) - }) - - t.Run("long column partially merged #1", func(t *testing.T) { - tw := NewWriter() - tw.AppendHeader(Row{"Column 1", "Column 2", "Column 3", "Column 4", "Column 5", "Column 6", "Column 7", "Column 8"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 1", "4F8F5CB531E3D49A61CF417CD133792CCFA501FD8DA53EE368FED20E5FE0248C3A0B64F98A6533CEE1DA614C3A8DDEC791FF05FEE6D971D57C1348320F4EB42DR", "4F8F5CB531E3D49A61CF417CD133792CCFA501FD8DA53EE368FED20E5FE0248C3A0B64F98A6533CEE1DA614C3A8DDEC791FF05FEE6D971D57C1348320F4EB42DR", "4F8F5CB531E3D49A61CF417CD133792CCFA501FD8DA53EE368FED20E5FE0248C3A0B64F98A6533CEE1DA614C3A8DDEC791FF05FEE6D971D57C1348320F4EB42DRR", "4F8F5CB531E3D49A61CF417CD133792CCFA501FD8DA53EE368FED20E5FE0248C3A0B64F98A6533CEE1DA614C3A8DDEC791FF05FEE6D971D57C1348320F4EB42DRR"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "Y", "Y", "Y"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "Y", "Y", "Y"}, rcAutoMerge) - tw.SetAutoIndex(true) - tw.SetColumnConfigs([]ColumnConfig{ - {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 24, WidthMaxEnforcer: text.WrapHard}, - {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 24, WidthMaxEnforcer: text.WrapHard}, - {Number: 7, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 24, WidthMaxEnforcer: text.WrapHard}, - {Number: 8, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 24, WidthMaxEnforcer: text.WrapHard}, - }) - tw.SetStyle(StyleLight) - tw.Style().Options.SeparateRows = true - - compareOutput(t, tw.Render(), ` -┌───┬──────────┬──────────┬──────────┬──────────┬─────────────┬─────────────┬─────────────┬─────────────┐ -│ │ COLUMN 1 │ COLUMN 2 │ COLUMN 3 │ COLUMN 4 │ COLUMN 5 │ COLUMN 6 │ COLUMN 7 │ COLUMN 8 │ -├───┼──────────┼──────────┼──────────┼──────────┼─────────────┴─────────────┼─────────────┴─────────────┤ -│ 1 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 1 │ 4F8F5CB531E3D49A61CF417C │ 4F8F5CB531E3D49A61CF417C │ -│ │ │ │ │ │ D133792CCFA501FD8DA53EE3 │ D133792CCFA501FD8DA53EE3 │ -│ │ │ │ │ │ 68FED20E5FE0248C3A0B64F9 │ 68FED20E5FE0248C3A0B64F9 │ -│ │ │ │ │ │ 8A6533CEE1DA614C3A8DDEC7 │ 8A6533CEE1DA614C3A8DDEC7 │ -│ │ │ │ │ │ 91FF05FEE6D971D57C134832 │ 91FF05FEE6D971D57C134832 │ -│ │ │ │ │ │ 0F4EB42DR │ 0F4EB42DRR │ -├───┼──────────┼──────────┼──────────┼──────────┼───────────────────────────┴───────────────────────────┤ -│ 2 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 2 │ Y │ -├───┼──────────┼──────────┼──────────┼──────────┼───────────────────────────────────────────────────────┤ -│ 3 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 2 │ Y │ -└───┴──────────┴──────────┴──────────┴──────────┴───────────────────────────────────────────────────────┘`) - }) - - t.Run("long column partially merged #2", func(t *testing.T) { - tw := NewWriter() - tw.AppendHeader(Row{"Column 1", "Column 2", "Column 3", "Column 4", "Column 5", "Column 6", "Column 7", "Column 8"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 1", "4F8F5CB531E3D49A61CF417CD133792CCFA501FD8DA53EE368FED20E5FE0248C3A0B64F98A6533CEE1DA614C3A8DDEC791FF05FEE6D971D57C1348320F4EB42DR", "4F8F5CB531E3D49A61CF417CD133792CCFA501FD8DA53EE368FED20E5FE0248C3A0B64F98A6533CEE1DA614C3A8DDEC791FF05FEE6D971D57C1348320F4EB42DR", "4F8F5CB531E3D49A61CF417CD133792CCFA501FD8DA53EE368FED20E5FE0248C3A0B64F98A6533CEE1DA614C3A8DDEC791FF05FEE6D971D57C1348320F4EB42DR", "4F8F5CB531E3D49A61CF417CD133792CCFA501FD8DA53EE368FED20E5FE0248C3A0B64F98A6533CEE1DA614C3A8DDEC791FF05FEE6D971D57C1348320F4EB42DRE"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "Y", "Y", "Y"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "Y", "Y", "Y"}, rcAutoMerge) - tw.SetAutoIndex(true) - tw.SetColumnConfigs([]ColumnConfig{ - {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 24, WidthMaxEnforcer: text.WrapHard}, - {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 24, WidthMaxEnforcer: text.WrapHard}, - {Number: 7, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 24, WidthMaxEnforcer: text.WrapHard}, - {Number: 8, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 24, WidthMaxEnforcer: text.WrapHard}, - }) - tw.SetStyle(StyleLight) - tw.Style().Options.SeparateRows = true - - compareOutput(t, tw.Render(), ` -┌───┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────────────────────┐ -│ │ COLUMN 1 │ COLUMN 2 │ COLUMN 3 │ COLUMN 4 │ COLUMN 5 │ COLUMN 6 │ COLUMN 7 │ COLUMN 8 │ -├───┼──────────┼──────────┼──────────┼──────────┼──────────┴──────────┴──────────┼──────────────────────────┤ -│ 1 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 1 │ 4F8F5CB531E3D49A61CF417C │ 4F8F5CB531E3D49A61CF417C │ -│ │ │ │ │ │ D133792CCFA501FD8DA53EE3 │ D133792CCFA501FD8DA53EE3 │ -│ │ │ │ │ │ 68FED20E5FE0248C3A0B64F9 │ 68FED20E5FE0248C3A0B64F9 │ -│ │ │ │ │ │ 8A6533CEE1DA614C3A8DDEC7 │ 8A6533CEE1DA614C3A8DDEC7 │ -│ │ │ │ │ │ 91FF05FEE6D971D57C134832 │ 91FF05FEE6D971D57C134832 │ -│ │ │ │ │ │ 0F4EB42DR │ 0F4EB42DRE │ -├───┼──────────┼──────────┼──────────┼──────────┼────────────────────────────────┴──────────────────────────┤ -│ 2 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 2 │ Y │ -├───┼──────────┼──────────┼──────────┼──────────┼───────────────────────────────────────────────────────────┤ -│ 3 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 2 │ Y │ -└───┴──────────┴──────────┴──────────┴──────────┴───────────────────────────────────────────────────────────┘`) - }) - - t.Run("long column fully merged", func(t *testing.T) { - tw := NewWriter() - tw.AppendHeader(Row{"Column 1", "Column 2", "Column 3", "Column 4", "Column 5", "Column 6", "Column 7", "Column 8"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 1", "4F8F5CB531E3D49A61CF417CD133792CCFA501FD8DA53EE368FED20E5FE0248C3A0B64F98A6533CEE1DA614C3A8DDEC791FF05FEE6D971D57C1348320F4EB42DR", "4F8F5CB531E3D49A61CF417CD133792CCFA501FD8DA53EE368FED20E5FE0248C3A0B64F98A6533CEE1DA614C3A8DDEC791FF05FEE6D971D57C1348320F4EB42DR", "4F8F5CB531E3D49A61CF417CD133792CCFA501FD8DA53EE368FED20E5FE0248C3A0B64F98A6533CEE1DA614C3A8DDEC791FF05FEE6D971D57C1348320F4EB42DR", "4F8F5CB531E3D49A61CF417CD133792CCFA501FD8DA53EE368FED20E5FE0248C3A0B64F98A6533CEE1DA614C3A8DDEC791FF05FEE6D971D57C1348320F4EB42DR"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "Y", "Y", "Y"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "Y", "Y", "Y"}, rcAutoMerge) - tw.SetAutoIndex(true) - tw.SetColumnConfigs([]ColumnConfig{ - {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 24, WidthMaxEnforcer: text.WrapHard}, - {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 24, WidthMaxEnforcer: text.WrapHard}, - {Number: 7, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 24, WidthMaxEnforcer: text.WrapHard}, - {Number: 8, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 24, WidthMaxEnforcer: text.WrapHard}, - }) - tw.SetStyle(StyleLight) - tw.Style().Options.SeparateRows = true - - compareOutput(t, tw.Render(), ` -┌───┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┐ -│ │ COLUMN 1 │ COLUMN 2 │ COLUMN 3 │ COLUMN 4 │ COLUMN 5 │ COLUMN 6 │ COLUMN 7 │ COLUMN 8 │ -├───┼──────────┼──────────┼──────────┼──────────┼──────────┴──────────┴──────────┴──────────┤ -│ 1 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 1 │ 4F8F5CB531E3D49A61CF417C │ -│ │ │ │ │ │ D133792CCFA501FD8DA53EE3 │ -│ │ │ │ │ │ 68FED20E5FE0248C3A0B64F9 │ -│ │ │ │ │ │ 8A6533CEE1DA614C3A8DDEC7 │ -│ │ │ │ │ │ 91FF05FEE6D971D57C134832 │ -│ │ │ │ │ │ 0F4EB42DR │ -├───┼──────────┼──────────┼──────────┼──────────┼───────────────────────────────────────────┤ -│ 2 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 2 │ Y │ -├───┼──────────┼──────────┼──────────┼──────────┼───────────────────────────────────────────┤ -│ 3 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 2 │ Y │ -└───┴──────────┴──────────┴──────────┴──────────┴───────────────────────────────────────────┘`) - }) - - t.Run("headers too", func(t *testing.T) { - tw := NewWriter() - tw.AppendHeader(Row{"Node IP", "Pods", "Namespace", "Container", "RCE", "RCE"}, rcAutoMerge) - tw.AppendHeader(Row{"", "", "", "", "EXE EXE EXE", "EXE EXE EXE"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 1", "Y", "Y"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "N"}) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1B", "C 3", "N", "N"}) - tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 4", "N", "N"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 5", "Y", "N"}) - tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 6", "Y", "Y"}, rcAutoMerge) - tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 7", "Y", "Y"}, rcAutoMerge) - tw.AppendFooter(Row{"", "", "", 7, 5, 3}, rcAutoMerge) - tw.AppendFooter(Row{"", "", "", 6, 4, 4}, rcAutoMerge) - tw.SetAutoIndex(true) - tw.SetColumnConfigs([]ColumnConfig{ - {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 7, WidthMaxEnforcer: text.WrapHard}, - {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 7, WidthMaxEnforcer: text.WrapHard}, - }) - tw.SetStyle(StyleLight) - tw.Style().Options.SeparateRows = true - - compareOutput(t, tw.Render(), ` -┌───┬─────────┬────────┬───────────┬───────────┬───────────┐ -│ │ NODE IP │ PODS │ NAMESPACE │ CONTAINER │ RCE │ -│ ├─────────┴────────┴───────────┴───────────┼───────────┤ -│ │ │ EXE EXE │ -│ │ │ EXE │ -├───┼─────────┬────────┬───────────┬───────────┼───────────┤ -│ 1 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 1 │ Y │ -├───┼─────────┼────────┼───────────┼───────────┼─────┬─────┤ -│ 2 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 2 │ Y │ N │ -├───┼─────────┼────────┼───────────┼───────────┼─────┼─────┤ -│ 3 │ 1.1.1.1 │ Pod 1A │ NS 1B │ C 3 │ N │ N │ -├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┤ -│ 4 │ 1.1.1.1 │ Pod 1B │ NS 2 │ C 4 │ N │ -├───┼─────────┼────────┼───────────┼───────────┼─────┬─────┤ -│ 5 │ 1.1.1.1 │ Pod 1B │ NS 2 │ C 5 │ Y │ N │ -├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┤ -│ 6 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 6 │ Y │ -├───┼─────────┼────────┼───────────┼───────────┼───────────┤ -│ 7 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 7 │ Y │ -├───┼─────────┴────────┴───────────┼───────────┼─────┬─────┤ -│ │ │ 7 │ 5 │ 3 │ -│ ├──────────────────────────────┼───────────┼─────┴─────┤ -│ │ │ 6 │ 4 │ -└───┴──────────────────────────────┴───────────┴───────────┘`) - }) - - t.Run("headers and footers too", func(t *testing.T) { - tw := NewWriter() - tw.AppendHeader(Row{"Node IP", "Pods", "Namespace", "Container", "RCE1", "RCE2"}, rcAutoMerge) - tw.AppendHeader(Row{"", "", "", "", "EXE EXE EXE", "EXE EXE EXE"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 1", "Y", "Y"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "Y"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1B", "C 3", "N", "N"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 4", "N", "N"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 5", "Y", "Y"}, rcAutoMerge) - tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 6", "Y", "Y"}, rcAutoMerge) - tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 7", "Y", "Y"}, rcAutoMerge) - tw.AppendFooter(Row{"", "", "", 7, 5, 5}, rcAutoMerge) - tw.AppendFooter(Row{"", "", "", 6, 4, 4}, rcAutoMerge) - tw.SetAutoIndex(true) - tw.SetColumnConfigs([]ColumnConfig{ - {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 7, WidthMaxEnforcer: text.WrapHard}, - {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 7, WidthMaxEnforcer: text.WrapHard}, - }) - tw.SetStyle(StyleLight) - tw.Style().Options.SeparateRows = true - - compareOutput(t, tw.Render(), ` -┌───┬─────────┬────────┬───────────┬───────────┬──────┬──────┐ -│ │ NODE IP │ PODS │ NAMESPACE │ CONTAINER │ RCE1 │ RCE2 │ -│ ├─────────┴────────┴───────────┴───────────┼──────┴──────┤ -│ │ │ EXE EXE │ -│ │ │ EXE │ -├───┼─────────┬────────┬───────────┬───────────┼─────────────┤ -│ 1 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 1 │ Y │ -├───┼─────────┼────────┼───────────┼───────────┼─────────────┤ -│ 2 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 2 │ Y │ -├───┼─────────┼────────┼───────────┼───────────┼─────────────┤ -│ 3 │ 1.1.1.1 │ Pod 1A │ NS 1B │ C 3 │ N │ -├───┼─────────┼────────┼───────────┼───────────┼─────────────┤ -│ 4 │ 1.1.1.1 │ Pod 1B │ NS 2 │ C 4 │ N │ -├───┼─────────┼────────┼───────────┼───────────┼─────────────┤ -│ 5 │ 1.1.1.1 │ Pod 1B │ NS 2 │ C 5 │ Y │ -├───┼─────────┼────────┼───────────┼───────────┼─────────────┤ -│ 6 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 6 │ Y │ -├───┼─────────┼────────┼───────────┼───────────┼─────────────┤ -│ 7 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 7 │ Y │ -├───┼─────────┴────────┴───────────┼───────────┼─────────────┤ -│ │ │ 7 │ 5 │ -│ ├──────────────────────────────┼───────────┼─────────────┤ -│ │ │ 6 │ 4 │ -└───┴──────────────────────────────┴───────────┴─────────────┘`) - }) - - t.Run("long header column", func(t *testing.T) { - tw := NewWriter() - tw.AppendHeader(Row{"Node IP", "Pods", "Namespace", "Container", "RCE1", "RCE2", "RCE3"}, rcAutoMerge) - tw.AppendHeader(Row{"", "", "", "", "EXE EXE EXE", "EXE EXE EXE", "EXE EXE EXE"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 1", "Y", "Y", "Y"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "Y", "Y"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1B", "C 3", "N", "N", "N"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 4", "N", "N", "N"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 5", "Y", "Y", "Y"}, rcAutoMerge) - tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 6", "Y", "Y", "Y"}, rcAutoMerge) - tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 7", "Y", "Y", "Y"}, rcAutoMerge) - tw.AppendFooter(Row{"", "", "", 7, 5, 5, 5}, rcAutoMerge) - tw.AppendFooter(Row{"", "", "", 6, 4, 4, 3}, rcAutoMerge) - tw.SetAutoIndex(true) - tw.SetColumnConfigs([]ColumnConfig{ - {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 7, WidthMaxEnforcer: text.WrapHard}, - {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 7, WidthMaxEnforcer: text.WrapHard}, - {Number: 7, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 7, WidthMaxEnforcer: text.WrapHard}, - }) - tw.SetStyle(StyleLight) - tw.Style().Options.SeparateRows = true - - compareOutput(t, tw.Render(), ` -┌───┬─────────┬────────┬───────────┬───────────┬──────┬──────┬──────┐ -│ │ NODE IP │ PODS │ NAMESPACE │ CONTAINER │ RCE1 │ RCE2 │ RCE3 │ -│ ├─────────┴────────┴───────────┴───────────┼──────┴──────┴──────┤ -│ │ │ EXE EXE │ -│ │ │ EXE │ -├───┼─────────┬────────┬───────────┬───────────┼────────────────────┤ -│ 1 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 1 │ Y │ -├───┼─────────┼────────┼───────────┼───────────┼────────────────────┤ -│ 2 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 2 │ Y │ -├───┼─────────┼────────┼───────────┼───────────┼────────────────────┤ -│ 3 │ 1.1.1.1 │ Pod 1A │ NS 1B │ C 3 │ N │ -├───┼─────────┼────────┼───────────┼───────────┼────────────────────┤ -│ 4 │ 1.1.1.1 │ Pod 1B │ NS 2 │ C 4 │ N │ -├───┼─────────┼────────┼───────────┼───────────┼────────────────────┤ -│ 5 │ 1.1.1.1 │ Pod 1B │ NS 2 │ C 5 │ Y │ -├───┼─────────┼────────┼───────────┼───────────┼────────────────────┤ -│ 6 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 6 │ Y │ -├───┼─────────┼────────┼───────────┼───────────┼────────────────────┤ -│ 7 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 7 │ Y │ -├───┼─────────┴────────┴───────────┼───────────┼────────────────────┤ -│ │ │ 7 │ 5 │ -│ ├──────────────────────────────┼───────────┼─────────────┬──────┤ -│ │ │ 6 │ 4 │ 3 │ -└───┴──────────────────────────────┴───────────┴─────────────┴──────┘`) - }) - - t.Run("everything", func(t *testing.T) { - tw := NewWriter() - tw.AppendHeader(Row{"COLUMNS", "COLUMNS", "COLUMNS", "COLUMNS", "COLUMNS", "COLUMNS", "COLUMNS"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 1", "Y", "Y", "Y"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "Y", "N"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1A", "NS 1B", "C 3", "N", "N", "N"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 4", "N", "Y", "N"}, rcAutoMerge) - tw.AppendRow(Row{"1.1.1.1", "Pod 1B", "NS 2", "C 5", "Y", "Y", "Y"}, rcAutoMerge) - tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 6", "N", "Y", "Y"}, rcAutoMerge) - tw.AppendRow(Row{"2.2.2.2", "Pod 2", "NS 3", "C 7", "Y", "Y", "Y"}, rcAutoMerge) - tw.AppendFooter(Row{"foo", "foo", "foo", "foo", "bar", "bar", "bar"}, rcAutoMerge) - tw.AppendFooter(Row{7, 7, 7, 7, 7, 7, 7}, rcAutoMerge) - tw.SetAutoIndex(true) - tw.SetColumnConfigs([]ColumnConfig{ - {Number: 1, AutoMerge: true}, - {Number: 2, AutoMerge: true}, - {Number: 3, AutoMerge: true}, - {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 7, WidthMaxEnforcer: text.WrapHard}, - {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 7, WidthMaxEnforcer: text.WrapHard}, - {Number: 7, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter, WidthMax: 7, WidthMaxEnforcer: text.WrapHard}, - }) - tw.SetStyle(StyleLight) - tw.Style().Options.SeparateRows = true - - compareOutput(t, tw.Render(), ` -┌───┬───────────────────────────────────────────────────┐ -│ │ COLUMNS │ -├───┼─────────┬─────────┬─────────┬─────────┬───────────┤ -│ 1 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 1 │ Y │ -├───┤ │ │ ├─────────┼───────┬───┤ -│ 2 │ │ │ │ C 2 │ Y │ N │ -├───┤ │ ├─────────┼─────────┼───────┴───┤ -│ 3 │ │ │ NS 1B │ C 3 │ N │ -├───┤ ├─────────┼─────────┼─────────┼───┬───┬───┤ -│ 4 │ │ Pod 1B │ NS 2 │ C 4 │ N │ Y │ N │ -├───┤ │ │ ├─────────┼───┴───┴───┤ -│ 5 │ │ │ │ C 5 │ Y │ -├───┼─────────┼─────────┼─────────┼─────────┼───┬───────┤ -│ 6 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 6 │ N │ Y │ -├───┤ │ │ ├─────────┼───┴───────┤ -│ 7 │ │ │ │ C 7 │ Y │ -├───┼─────────┴─────────┴─────────┴─────────┼───────────┤ -│ │ FOO │ BAR │ -│ ├───────────────────────────────────────┴───────────┤ -│ │ 7 │ -└───┴───────────────────────────────────────────────────┘`) - }) -} - func TestTable_Render_BorderAndSeparators(t *testing.T) { table := Table{} table.AppendHeader(testHeader) diff --git a/table/table.go b/table/table.go index 1801c1c..6b7c906 100644 --- a/table/table.go +++ b/table/table.go @@ -164,14 +164,15 @@ func (t *Table) AppendRows(rows []Row, config ...RowConfig) { // append is a separator, it will not be rendered in addition to the usual table // separator. // -//****************************************************************************** +// ****************************************************************************** // Please note the following caveats: -// 1. SetPageSize(): this may end up creating consecutive separator rows near -// the end of a page or at the beginning of a page -// 2. SortBy(): since SortBy could inherently alter the ordering of rows, the -// separators may not appear after the row it was originally intended to -// follow -//****************************************************************************** +// 1. SetPageSize(): this may end up creating consecutive separator rows near +// the end of a page or at the beginning of a page +// 2. SortBy(): since SortBy could inherently alter the ordering of rows, the +// separators may not appear after the row it was originally intended to +// follow +// +// ****************************************************************************** func (t *Table) AppendSeparator() { if t.separators == nil { t.separators = make(map[int]bool) @@ -439,12 +440,9 @@ func (t *Table) getColumnSeparator(row rowStr, colIdx int, hint renderHint) stri separator = t.style.Box.BottomSeparator } } else { - separator = t.getColumnSeparatorNonBorder( - t.shouldMergeCellsHorizontallyAbove(row, colIdx, hint), - t.shouldMergeCellsHorizontallyBelow(row, colIdx, hint), - colIdx, - hint, - ) + sm1 := t.shouldMergeCellsHorizontallyAbove(row, colIdx, hint) + sm2 := t.shouldMergeCellsHorizontallyBelow(row, colIdx, hint) + separator = t.getColumnSeparatorNonBorder(sm1, sm2, colIdx, hint) } } return separator @@ -721,7 +719,10 @@ func (t *Table) shouldMergeCellsHorizontallyBelow(row rowStr, colIdx int, hint r var rowConfig RowConfig if hint.isSeparatorRow { - if hint.isHeaderRow && hint.rowNumber == 0 { + if hint.isRegularRow() { + rowConfig = t.getRowConfig(renderHint{rowNumber: hint.rowNumber + 1}) + row = t.getRow(hint.rowNumber, renderHint{}) + } else if hint.isHeaderRow && hint.rowNumber == 0 { rowConfig = t.getRowConfig(renderHint{isHeaderRow: true, rowNumber: 1}) row = t.getRow(0, hint) } else if hint.isHeaderRow && hint.isLastRow { @@ -733,9 +734,6 @@ func (t *Table) shouldMergeCellsHorizontallyBelow(row rowStr, colIdx int, hint r } else if hint.isFooterRow && hint.rowNumber >= 0 { rowConfig = t.getRowConfig(renderHint{isFooterRow: true, rowNumber: 1}) row = t.getRow(hint.rowNumber, renderHint{isFooterRow: true}) - } else if hint.isRegularRow() { - rowConfig = t.getRowConfig(renderHint{rowNumber: hint.rowNumber + 1}) - row = t.getRow(hint.rowNumber, renderHint{}) } } @@ -751,13 +749,13 @@ func (t *Table) shouldMergeCellsVertically(colIdx int, hint renderHint) bool { rowPrev := t.getRow(hint.rowNumber-1, hint) rowNext := t.getRow(hint.rowNumber, hint) if colIdx < len(rowPrev) && colIdx < len(rowNext) { - return rowPrev[colIdx] == rowNext[colIdx] || rowNext[colIdx] == "" + return rowPrev[colIdx] == rowNext[colIdx] } } else { rowPrev := t.getRow(hint.rowNumber-2, hint) rowCurr := t.getRow(hint.rowNumber-1, hint) if colIdx < len(rowPrev) && colIdx < len(rowCurr) { - return rowPrev[colIdx] == rowCurr[colIdx] || rowCurr[colIdx] == "" + return rowPrev[colIdx] == rowCurr[colIdx] } } } diff --git a/text/align.go b/text/align.go index 764a1f8..7a6d92c 100644 --- a/text/align.go +++ b/text/align.go @@ -20,11 +20,11 @@ const ( ) // Apply aligns the text as directed. For ex.: -// * AlignDefault.Apply("Jon Snow", 12) returns "Jon Snow " -// * AlignLeft.Apply("Jon Snow", 12) returns "Jon Snow " -// * AlignCenter.Apply("Jon Snow", 12) returns " Jon Snow " -// * AlignJustify.Apply("Jon Snow", 12) returns "Jon Snow" -// * AlignRight.Apply("Jon Snow", 12) returns " Jon Snow" +// - AlignDefault.Apply("Jon Snow", 12) returns "Jon Snow " +// - AlignLeft.Apply("Jon Snow", 12) returns "Jon Snow " +// - AlignCenter.Apply("Jon Snow", 12) returns " Jon Snow " +// - AlignJustify.Apply("Jon Snow", 12) returns "Jon Snow" +// - AlignRight.Apply("Jon Snow", 12) returns " Jon Snow" func (a Align) Apply(text string, maxLength int) string { text = a.trimString(text) sLen := utf8.RuneCountInString(text) diff --git a/text/ansi.go b/text/ansi.go index aaf5b30..6a396b1 100644 --- a/text/ansi.go +++ b/text/ansi.go @@ -8,7 +8,8 @@ var ANSICodesSupported = areANSICodesSupported() // Escape encodes the string with the ANSI Escape Sequence. // For ex.: -// Escape("Ghost", "") == "Ghost" +// +// Escape("Ghost", "") == "Ghost" // Escape("Ghost", "\x1b[91m") == "\x1b[91mGhost\x1b[0m" // Escape("\x1b[94mGhost\x1b[0mLady", "\x1b[91m") == "\x1b[94mGhost\x1b[0m\x1b[91mLady\x1b[0m" // Escape("Nymeria\x1b[94mGhost\x1b[0mLady", "\x1b[91m") == "\x1b[91mNymeria\x1b[94mGhost\x1b[0m\x1b[91mLady\x1b[0m" @@ -30,7 +31,8 @@ func Escape(str string, escapeSeq string) string { // StripEscape strips all ANSI Escape Sequence from the string. // For ex.: -// StripEscape("Ghost") == "Ghost" +// +// StripEscape("Ghost") == "Ghost" // StripEscape("\x1b[91mGhost\x1b[0m") == "Ghost" // StripEscape("\x1b[94mGhost\x1b[0m\x1b[91mLady\x1b[0m") == "GhostLady" // StripEscape("\x1b[91mNymeria\x1b[94mGhost\x1b[0m\x1b[91mLady\x1b[0m") == "NymeriaGhostLady" diff --git a/text/string.go b/text/string.go index 6a21dda..9f5c9e8 100644 --- a/text/string.go +++ b/text/string.go @@ -13,11 +13,12 @@ var ( ) // InsertEveryN inserts the rune every N characters in the string. For ex.: -// InsertEveryN("Ghost", '-', 1) == "G-h-o-s-t" -// InsertEveryN("Ghost", '-', 2) == "Gh-os-t" -// InsertEveryN("Ghost", '-', 3) == "Gho-st" -// InsertEveryN("Ghost", '-', 4) == "Ghos-t" -// InsertEveryN("Ghost", '-', 5) == "Ghost" +// +// InsertEveryN("Ghost", '-', 1) == "G-h-o-s-t" +// InsertEveryN("Ghost", '-', 2) == "Gh-os-t" +// InsertEveryN("Ghost", '-', 3) == "Gho-st" +// InsertEveryN("Ghost", '-', 4) == "Ghos-t" +// InsertEveryN("Ghost", '-', 5) == "Ghost" func InsertEveryN(str string, runeToInsert rune, n int) string { if n <= 0 { return str @@ -47,7 +48,8 @@ func InsertEveryN(str string, runeToInsert rune, n int) string { // LongestLineLen returns the length of the longest "line" within the // argument string. For ex.: -// LongestLineLen("Ghost!\nCome back here!\nRight now!") == 15 +// +// LongestLineLen("Ghost!\nCome back here!\nRight now!") == 15 func LongestLineLen(str string) int { maxLength, currLength, eSeq := 0, 0, escSeq{} for _, c := range str { @@ -91,11 +93,12 @@ func OverrideRuneWidthEastAsianWidth(val bool) { // Pad pads the given string with as many characters as needed to make it as // long as specified (maxLen). This function does not count escape sequences // while calculating length of the string. Ex.: -// Pad("Ghost", 0, ' ') == "Ghost" -// Pad("Ghost", 3, ' ') == "Ghost" -// Pad("Ghost", 5, ' ') == "Ghost" -// Pad("Ghost", 7, ' ') == "Ghost " -// Pad("Ghost", 10, '.') == "Ghost....." +// +// Pad("Ghost", 0, ' ') == "Ghost" +// Pad("Ghost", 3, ' ') == "Ghost" +// Pad("Ghost", 5, ' ') == "Ghost" +// Pad("Ghost", 7, ' ') == "Ghost " +// Pad("Ghost", 10, '.') == "Ghost....." func Pad(str string, maxLen int, paddingChar rune) string { strLen := RuneWidthWithoutEscSequences(str) if strLen < maxLen { @@ -106,11 +109,12 @@ func Pad(str string, maxLen int, paddingChar rune) string { // RepeatAndTrim repeats the given string until it is as long as maxRunes. // For ex.: -// RepeatAndTrim("", 5) == "" -// RepeatAndTrim("Ghost", 0) == "" -// RepeatAndTrim("Ghost", 5) == "Ghost" -// RepeatAndTrim("Ghost", 7) == "GhostGh" -// RepeatAndTrim("Ghost", 10) == "GhostGhost" +// +// RepeatAndTrim("", 5) == "" +// RepeatAndTrim("Ghost", 0) == "" +// RepeatAndTrim("Ghost", 5) == "Ghost" +// RepeatAndTrim("Ghost", 7) == "GhostGh" +// RepeatAndTrim("Ghost", 10) == "GhostGhost" func RepeatAndTrim(str string, maxRunes int) string { if str == "" || maxRunes == 0 { return "" @@ -123,10 +127,12 @@ func RepeatAndTrim(str string, maxRunes int) string { // RuneCount is similar to utf8.RuneCountInString, except for the fact that it // ignores escape sequences while counting. For ex.: -// RuneCount("") == 0 -// RuneCount("Ghost") == 5 -// RuneCount("\x1b[33mGhost\x1b[0m") == 5 -// RuneCount("\x1b[33mGhost\x1b[0") == 5 +// +// RuneCount("") == 0 +// RuneCount("Ghost") == 5 +// RuneCount("\x1b[33mGhost\x1b[0m") == 5 +// RuneCount("\x1b[33mGhost\x1b[0") == 5 +// // Deprecated: in favor of RuneWidthWithoutEscSequences func RuneCount(str string) int { return RuneWidthWithoutEscSequences(str) @@ -135,21 +141,23 @@ func RuneCount(str string) int { // RuneWidth returns the mostly accurate character-width of the rune. This is // not 100% accurate as the character width is usually dependent on the // typeface (font) used in the console/terminal. For ex.: -// RuneWidth('A') == 1 -// RuneWidth('ツ') == 2 -// RuneWidth('⊙') == 1 -// RuneWidth('︿') == 2 -// RuneWidth(0x27) == 0 +// +// RuneWidth('A') == 1 +// RuneWidth('ツ') == 2 +// RuneWidth('⊙') == 1 +// RuneWidth('︿') == 2 +// RuneWidth(0x27) == 0 func RuneWidth(r rune) int { return rwCondition.RuneWidth(r) } // RuneWidthWithoutEscSequences is similar to RuneWidth, except for the fact // that it ignores escape sequences while counting. For ex.: -// RuneWidthWithoutEscSequences("") == 0 -// RuneWidthWithoutEscSequences("Ghost") == 5 -// RuneWidthWithoutEscSequences("\x1b[33mGhost\x1b[0m") == 5 -// RuneWidthWithoutEscSequences("\x1b[33mGhost\x1b[0") == 5 +// +// RuneWidthWithoutEscSequences("") == 0 +// RuneWidthWithoutEscSequences("Ghost") == 5 +// RuneWidthWithoutEscSequences("\x1b[33mGhost\x1b[0m") == 5 +// RuneWidthWithoutEscSequences("\x1b[33mGhost\x1b[0") == 5 func RuneWidthWithoutEscSequences(str string) int { count, eSeq := 0, escSeq{} for _, c := range str { @@ -166,12 +174,13 @@ func RuneWidthWithoutEscSequences(str string) int { } // Snip returns the given string with a fixed length. For ex.: -// Snip("Ghost", 0, "~") == "Ghost" -// Snip("Ghost", 1, "~") == "~" -// Snip("Ghost", 3, "~") == "Gh~" -// Snip("Ghost", 5, "~") == "Ghost" -// Snip("Ghost", 7, "~") == "Ghost " -// Snip("\x1b[33mGhost\x1b[0m", 7, "~") == "\x1b[33mGhost\x1b[0m " +// +// Snip("Ghost", 0, "~") == "Ghost" +// Snip("Ghost", 1, "~") == "~" +// Snip("Ghost", 3, "~") == "Gh~" +// Snip("Ghost", 5, "~") == "Ghost" +// Snip("Ghost", 7, "~") == "Ghost " +// Snip("\x1b[33mGhost\x1b[0m", 7, "~") == "\x1b[33mGhost\x1b[0m " func Snip(str string, length int, snipIndicator string) string { if length > 0 { lenStr := RuneWidthWithoutEscSequences(str) @@ -185,10 +194,11 @@ func Snip(str string, length int, snipIndicator string) string { // Trim trims a string to the given length while ignoring escape sequences. For // ex.: -// Trim("Ghost", 3) == "Gho" -// Trim("Ghost", 6) == "Ghost" -// Trim("\x1b[33mGhost\x1b[0m", 3) == "\x1b[33mGho\x1b[0m" -// Trim("\x1b[33mGhost\x1b[0m", 6) == "\x1b[33mGhost\x1b[0m" +// +// Trim("Ghost", 3) == "Gho" +// Trim("Ghost", 6) == "Ghost" +// Trim("\x1b[33mGhost\x1b[0m", 3) == "\x1b[33mGho\x1b[0m" +// Trim("\x1b[33mGhost\x1b[0m", 6) == "\x1b[33mGhost\x1b[0m" func Trim(str string, maxLen int) string { if maxLen <= 0 { return "" diff --git a/text/transformer.go b/text/transformer.go index 4551436..193a721 100644 --- a/text/transformer.go +++ b/text/transformer.go @@ -37,9 +37,9 @@ var ( type Transformer func(val interface{}) string // NewNumberTransformer returns a number Transformer that: -// * transforms the number as directed by 'format' (ex.: %.2f) -// * colors negative values Red -// * colors positive values Green +// - transforms the number as directed by 'format' (ex.: %.2f) +// - colors negative values Red +// - colors positive values Green func NewNumberTransformer(format string) Transformer { return func(val interface{}) string { if valStr := transformInt(format, val); valStr != "" { diff --git a/text/valign.go b/text/valign.go index 76b1943..f1a75e9 100644 --- a/text/valign.go +++ b/text/valign.go @@ -14,12 +14,12 @@ const ( ) // Apply aligns the lines vertically. For ex.: -// * VAlignTop.Apply({"Game", "Of", "Thrones"}, 5) -// returns {"Game", "Of", "Thrones", "", ""} -// * VAlignMiddle.Apply({"Game", "Of", "Thrones"}, 5) -// returns {"", "Game", "Of", "Thrones", ""} -// * VAlignBottom.Apply({"Game", "Of", "Thrones"}, 5) -// returns {"", "", "Game", "Of", "Thrones"} +// - VAlignTop.Apply({"Game", "Of", "Thrones"}, 5) +// returns {"Game", "Of", "Thrones", "", ""} +// - VAlignMiddle.Apply({"Game", "Of", "Thrones"}, 5) +// returns {"", "Game", "Of", "Thrones", ""} +// - VAlignBottom.Apply({"Game", "Of", "Thrones"}, 5) +// returns {"", "", "Game", "Of", "Thrones"} func (va VAlign) Apply(lines []string, maxLines int) []string { if len(lines) == maxLines { return lines @@ -42,12 +42,12 @@ func (va VAlign) Apply(lines []string, maxLines int) []string { } // ApplyStr aligns the string (of 1 or more lines) vertically. For ex.: -// * VAlignTop.ApplyStr("Game\nOf\nThrones", 5) -// returns {"Game", "Of", "Thrones", "", ""} -// * VAlignMiddle.ApplyStr("Game\nOf\nThrones", 5) -// returns {"", "Game", "Of", "Thrones", ""} -// * VAlignBottom.ApplyStr("Game\nOf\nThrones", 5) -// returns {"", "", "Game", "Of", "Thrones"} +// - VAlignTop.ApplyStr("Game\nOf\nThrones", 5) +// returns {"Game", "Of", "Thrones", "", ""} +// - VAlignMiddle.ApplyStr("Game\nOf\nThrones", 5) +// returns {"", "Game", "Of", "Thrones", ""} +// - VAlignBottom.ApplyStr("Game\nOf\nThrones", 5) +// returns {"", "", "Game", "Of", "Thrones"} func (va VAlign) ApplyStr(text string, maxLines int) []string { return va.Apply(strings.Split(text, "\n"), maxLines) }