-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathastab.go
73 lines (67 loc) · 1.71 KB
/
astab.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
package astab
import (
"fmt"
"io"
"reflect"
"strings"
)
type renderer struct {
colwidth []int
rows [][]*string
}
func (r *renderer) str(row, col int, val *string) {
if r.colwidth[col] < len(*val) {
r.colwidth[col] = len(*val)
}
if r.rows[row] == nil {
r.rows[row] = make([]*string, len(r.colwidth))
}
r.rows[row][col] = val
}
func (r *renderer) write(w io.Writer) {
for row := range r.rows {
for col := range r.rows[row] {
if col > 0 {
w.Write([]byte(" "))
}
w.Write([]byte(*r.rows[row][col]))
w.Write([]byte(strings.Repeat(" ", r.colwidth[col]-len(*r.rows[row][col]))))
}
w.Write([]byte("\n"))
}
}
// Write takes a slice of structs, calculates the maximum string width of each
// exported struct field value, and renders a table to write to any io.Writer.
func Write(w io.Writer, slice interface{}) error {
slcv := reflect.ValueOf(slice)
if slcv.Kind() != reflect.Slice {
return fmt.Errorf("expected slice, got %s", slcv.Kind())
}
r := renderer{rows: make([][]*string, 1+slcv.Len())}
el := slcv.Type().Elem()
if el.Kind() != reflect.Struct {
return fmt.Errorf("expected slice of struct, got slice of %s", el.Kind())
}
exported := make([]int, 0, el.NumField())
for j := 0; j < el.NumField(); j++ {
if el.Field(j).PkgPath == "" {
exported = append(exported, j)
}
}
if len(exported) == 0 {
return fmt.Errorf("struct %q does not have exported fields", el)
}
r.colwidth = make([]int, len(exported))
for j := 0; j < len(exported); j++ {
s := el.Field(exported[j]).Name
r.str(0, j, &s)
}
for j := 0; j < slcv.Len(); j++ {
for k := 0; k < len(exported); k++ {
s := fmt.Sprint(slcv.Index(j).Field(exported[k]).Interface())
r.str(j+1, k, &s)
}
}
r.write(w)
return nil
}