forked from filecoin-project/lotus
-
Notifications
You must be signed in to change notification settings - Fork 14
/
tablewriter.go
140 lines (114 loc) · 2.41 KB
/
tablewriter.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package tablewriter
import (
"fmt"
"io"
"strings"
"unicode/utf8"
"github.com/acarl005/stripansi"
)
type Column struct {
Name string
SeparateLine bool
Lines int
}
type TableWriter struct {
cols []Column
rows []map[int]string
}
func Col(name string) Column {
return Column{
Name: name,
SeparateLine: false,
}
}
func NewLineCol(name string) Column {
return Column{
Name: name,
SeparateLine: true,
}
}
// Unlike text/tabwriter, this works with CLI escape codes, and allows for info
// in separate lines
func New(cols ...Column) *TableWriter {
return &TableWriter{
cols: cols,
}
}
func (w *TableWriter) Write(r map[string]interface{}) {
// this can cause columns to be out of order, but will at least work
byColID := map[int]string{}
cloop:
for col, val := range r {
for i, column := range w.cols {
if column.Name == col {
byColID[i] = fmt.Sprint(val)
w.cols[i].Lines++
continue cloop
}
}
byColID[len(w.cols)] = fmt.Sprint(val)
w.cols = append(w.cols, Column{
Name: col,
SeparateLine: false,
Lines: 1,
})
}
w.rows = append(w.rows, byColID)
}
func (w *TableWriter) Flush(out io.Writer) error {
colLengths := make([]int, len(w.cols))
header := map[int]string{}
for i, col := range w.cols {
if col.SeparateLine {
continue
}
header[i] = col.Name
}
w.rows = append([]map[int]string{header}, w.rows...)
for col, c := range w.cols {
if c.Lines == 0 {
continue
}
for _, row := range w.rows {
val, found := row[col]
if !found {
continue
}
if cliStringLength(val) > colLengths[col] {
colLengths[col] = cliStringLength(val)
}
}
}
for _, row := range w.rows {
cols := make([]string, len(w.cols))
for ci, col := range w.cols {
if col.Lines == 0 {
continue
}
e, _ := row[ci]
pad := colLengths[ci] - cliStringLength(e) + 2
if !col.SeparateLine && col.Lines > 0 {
e = e + strings.Repeat(" ", pad)
if _, err := fmt.Fprint(out, e); err != nil {
return err
}
}
cols[ci] = e
}
if _, err := fmt.Fprintln(out); err != nil {
return err
}
for ci, col := range w.cols {
if !col.SeparateLine || len(cols[ci]) == 0 {
continue
}
if _, err := fmt.Fprintf(out, " %s: %s\n", col.Name, cols[ci]); err != nil {
return err
}
}
}
return nil
}
func cliStringLength(s string) (n int) {
return utf8.RuneCountInString(stripansi.Strip(s))
}