...
1 package utils
2
3
4
5 import (
6 "fmt"
7 "strings"
8 )
9
10 type Table struct {
11 addingRow int
12 rows []*Row
13 headerRow *Row
14 hidden map[int]bool
15 maxlenCol map[int]int
16 }
17
18 type Row struct {
19 t *Table
20 cells []*Cell
21 }
22
23 type Cell struct {
24 typ int
25 txt string
26 num uint64
27 ts uint32
28 f float64
29 b bool
30 snum int64
31 }
32
33 func (c *Cell) String() string {
34 if c.typ == 0 {
35 return ""
36 } else if c.typ == 1 {
37 return c.txt
38 } else if c.typ == 2 {
39 return fmt.Sprintf("%d", c.num)
40 } else if c.typ == 3 {
41 return TimestampString(c.ts)
42 } else if c.typ == 7 {
43 return TimestampAgeString(c.ts) + " (" + TimestampString(c.ts) + ")"
44 } else if c.typ == 4 {
45 return fmt.Sprintf("%0.2f", c.f)
46 } else if c.typ == 5 {
47 return fmt.Sprintf("%v", c.b)
48 } else if c.typ == 6 {
49 return fmt.Sprintf("%d", c.snum)
50 }
51 return fmt.Sprintf("type %d", c.typ)
52 }
53
54
55 func (t *Table) NewRow() {
56 t.addingRow++
57 }
58 func (t *Table) getHeaderRow() *Row {
59 if t.headerRow == nil {
60 t.headerRow = &Row{t: t}
61 }
62 return t.headerRow
63 }
64 func (t *Table) AddHeader(s string) {
65 t.getHeaderRow().AddCell(&Cell{typ: 1, txt: s})
66 }
67 func (t *Table) AddHeaders(s ...string) {
68 for _, a := range s {
69 t.getHeaderRow().AddCell(&Cell{typ: 1, txt: a})
70 }
71 }
72
73 func (t *Table) GetRowOrCreate(num int) *Row {
74 for len(t.rows) <= num {
75 t.rows = append(t.rows, &Row{t: t})
76 }
77 return t.rows[num]
78 }
79 func (t *Table) AddBool(b bool) *Table {
80 r := t.GetRowOrCreate(t.addingRow)
81 r.AddCell(&Cell{typ: 5, b: b})
82 return t
83 }
84 func (t *Table) AddString(s string) *Table {
85 r := t.GetRowOrCreate(t.addingRow)
86 r.AddCell(&Cell{typ: 1, txt: s})
87 return t
88 }
89 func (t *Table) AddStrings(sts ...string) *Table {
90 for _, s := range sts {
91 r := t.GetRowOrCreate(t.addingRow)
92 r.AddCell(&Cell{typ: 1, txt: s})
93 }
94 return t
95 }
96 func (t *Table) AddTimestamp(ts uint32) *Table {
97 r := t.GetRowOrCreate(t.addingRow)
98 r.AddCell(&Cell{typ: 3, ts: ts})
99 return t
100 }
101 func (t *Table) AddTimestampWithAge(ts uint32) *Table {
102 r := t.GetRowOrCreate(t.addingRow)
103 r.AddCell(&Cell{typ: 7, ts: ts})
104 return t
105 }
106 func (t *Table) AddFloat64(f float64) *Table {
107 r := t.GetRowOrCreate(t.addingRow)
108 r.AddCell(&Cell{typ: 4, f: f})
109 return t
110 }
111 func (t *Table) AddUint32(i uint32) *Table {
112 t.AddUint64(uint64(i))
113 return t
114 }
115 func (t *Table) AddInt(i int) *Table {
116 t.AddUint64(uint64(i))
117 return t
118 }
119 func (t *Table) AddInt64(i int64) *Table {
120 r := t.GetRowOrCreate(t.addingRow)
121 r.AddCell(&Cell{typ: 6, snum: i})
122 return t
123 }
124 func (t *Table) AddUint64(i uint64) *Table {
125 r := t.GetRowOrCreate(t.addingRow)
126 r.AddCell(&Cell{typ: 2, num: i})
127 return t
128
129 }
130 func (r *Row) AddCell(cell *Cell) {
131 r.cells = append(r.cells, cell)
132 }
133
134
135 func (r *Row) Cols() int {
136 return len(r.Cells())
137 }
138
139
140 func (r *Row) Cells() []*Cell {
141 if r.t.hidden == nil {
142 r.t.hidden = make(map[int]bool)
143 }
144 var res []*Cell
145 for i := 0; i < len(r.cells); i++ {
146 if r.t.hidden[i] {
147 continue
148 }
149 res = append(res, r.cells[i])
150 }
151 return res
152 }
153
154
155 func (r *Row) GetCell(idx int) *Cell {
156 col := r.t.idx2col(idx)
157
158 if len(r.cells) <= col {
159 return nil
160 }
161 return r.cells[col]
162 }
163
164 func (t *Table) ToCSV() string {
165 rows := len(t.rows)
166 sb := strings.Builder{}
167 for i := 0; i < rows; i++ {
168 row := t.GetRowOrCreate(i)
169 if row.Cols() == 0 {
170 continue
171 }
172 line := ""
173 deli := ""
174 for cn := 0; cn < row.Cols(); cn++ {
175 cel := row.GetCell(cn)
176 s := escapeCell(cel.String())
177 line = line + deli + s
178 deli = ","
179 }
180 sb.WriteString(line + "\n")
181 }
182 return sb.String()
183 }
184 func escapeCell(s string) string {
185 s = strings.ReplaceAll(s, ",", "\\,")
186 return s
187 }
188
189
190 func (t *Table) DisableColumn(col int) {
191 if t.hidden == nil {
192 t.hidden = make(map[int]bool)
193 }
194 t.hidden[col] = true
195 }
196
197
198 func (t *Table) EnableColumn(col int) {
199 if t.hidden == nil {
200 return
201 }
202 t.hidden[col] = false
203 }
204
205
206 func (t *Table) EnableAllColumns() {
207 t.hidden = nil
208 }
209
210
211 func (t *Table) idx2col(idx int) int {
212 if t.hidden == nil {
213 return idx
214 }
215 off := 0
216 for i := 0; i < idx; i++ {
217 if t.hidden[idx] {
218 off++
219 }
220 }
221 return idx + off
222 }
223 func (t *Table) GetMaxLen(col int) int {
224 if t.maxlenCol == nil {
225 t.maxlenCol = make(map[int]int)
226 }
227 f, found := t.maxlenCol[col]
228 if !found {
229 return 0xFFFFFFFF
230 }
231 return f
232
233 }
234 func (t *Table) SetMaxLen(col, width int) {
235 if t.maxlenCol == nil {
236 t.maxlenCol = make(map[int]int)
237 }
238 t.maxlenCol[col] = width
239 }
240
241
242 func (t *Table) GetPrintingRows() []*Row {
243 var res []*Row
244 for _, r := range t.rows {
245 res = append(res, r.toPrintingRows()...)
246 }
247 return res
248 }
249
250
251 func (r *Row) toPrintingRows() []*Row {
252 var res []*Row
253 for cn, c := range r.Cells() {
254 if c.typ != 1 {
255
256 if len(res) == 0 {
257 res = append(res, &Row{t: r.t})
258 }
259 r := res[0]
260 for len(r.cells) <= cn {
261 r.cells = append(r.cells, &Cell{})
262 }
263 r.cells[cn] = c
264 continue
265 }
266
267
268 ml := r.t.GetMaxLen(cn)
269 s := c.String()
270 lines := strings.Split(s, "\n")
271 var tlines []string
272 for _, l := range lines {
273 for {
274 if len(l) <= ml {
275 tlines = append(tlines, l)
276 break
277 }
278 splitAt := findBetterSplitAt(l, ml)
279 nl := l[:splitAt]
280 tlines = append(tlines, nl)
281 l = l[splitAt:]
282 }
283 }
284 for len(res) < len(tlines) {
285 res = append(res, &Row{t: r.t})
286 }
287 for i, l := range tlines {
288 r := res[i]
289 for len(r.cells) <= cn {
290 r.cells = append(r.cells, &Cell{})
291 }
292 r.cells[cn] = &Cell{typ: c.typ, txt: l}
293
294 }
295 }
296 return res
297 }
298 func findBetterSplitAt(line string, proposed int) int {
299 splits := []byte{' ', ':'}
300 i := proposed
301 j := 0
302 for {
303 j++
304 if j > 20 {
305 return proposed
306 }
307 i--
308 if i < 0 {
309 return proposed
310 }
311 for _, sp := range splits {
312 if line[i] == sp {
313 if i < proposed {
314 return i + 1
315 }
316 return i
317 }
318 }
319
320 }
321
322 }
323
View as plain text