...
1 package table
2
3 import (
4 "fmt"
5 "io"
6 "sort"
7 "strings"
8 )
9
10 type (
11
12 Table struct {
13 Columns []Column
14 Data []Row
15 Sort []int
16 ColumnSpacing string
17 }
18
19
20 Row = []string
21
22
23 Column struct {
24 Header string
25 Width int
26
27 Hide bool
28
29 Flexible bool
30 LeftAlign bool
31 }
32 )
33
34 const defaultColumnSpacing = " "
35
36
37 func NewTable(cols []Column, data []Row) Table {
38 return Table{
39 Columns: cols,
40 Data: data,
41 Sort: []int{},
42 ColumnSpacing: defaultColumnSpacing,
43 }
44 }
45
46
47 func NewColumn(header string) Column {
48 return Column{
49 Header: header,
50 Flexible: true,
51 Width: len(header),
52 }
53 }
54
55
56 func (c Column) WithLeftAlign() Column {
57 c.LeftAlign = true
58 return c
59 }
60
61
62 func (t *Table) Render(w io.Writer) {
63 columnWidths := t.columnWidths()
64 t.renderRow(w, t.headerRow(), columnWidths)
65 t.sort()
66 for _, row := range t.Data {
67 t.renderRow(w, row, columnWidths)
68 }
69 }
70
71 func (t *Table) columnWidths() []int {
72 widths := make([]int, len(t.Columns))
73 for c, col := range t.Columns {
74 width := col.Width
75 if col.Flexible {
76 for _, row := range t.Data {
77 if len(row[c]) > width {
78 width = len(row[c])
79 }
80 }
81 }
82 widths[c] = width
83 }
84 return widths
85 }
86
87 func (t *Table) sort() {
88 if len(t.Sort) == 0 {
89 return
90 }
91 sort.Slice(t.Data, func(i, j int) bool {
92 for _, sortCol := range t.Sort {
93 if t.Data[i][sortCol] < t.Data[j][sortCol] {
94 return true
95 } else if t.Data[i][sortCol] > t.Data[j][sortCol] {
96 return false
97 }
98 }
99 return false
100 })
101 }
102
103 func (t *Table) renderRow(w io.Writer, row Row, columnWidths []int) {
104 for c, col := range t.Columns {
105 if col.Hide {
106 continue
107 }
108 value := row[c]
109 if len(value) > columnWidths[c] {
110 value = value[:columnWidths[c]]
111 }
112 padding := strings.Repeat(" ", columnWidths[c]-len(value))
113 if col.LeftAlign {
114 fmt.Fprintf(w, "%s%s%s", value, padding, t.ColumnSpacing)
115 } else {
116 fmt.Fprintf(w, "%s%s%s", padding, value, t.ColumnSpacing)
117 }
118 }
119 fmt.Fprint(w, "\n")
120 }
121
122 func (t *Table) headerRow() Row {
123 row := make(Row, len(t.Columns))
124 for c, col := range t.Columns {
125 row[c] = col.Header
126 }
127 return row
128 }
129
View as plain text