-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstructrowsviewer.go
140 lines (119 loc) · 3.64 KB
/
structrowsviewer.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 retable
import (
"fmt"
"reflect"
)
// Ensure StructRowsViewer implements Viewer
var _ Viewer = new(StructRowsViewer)
// StructRowsViewer implements Viewer for tables
// represented by a slice or array of struct rows.
type StructRowsViewer struct {
StructFieldNaming
// MapIndices is a map from the index of a field in struct
// to the column index returned by the function StructFieldTypes.
// If MapIndices is nil, then no mapping will be performed.
// Mapping a struct field index to -1 will ignore this field
// and not create a column for it..
MapIndices map[int]int
}
func (v *StructRowsViewer) clone() *StructRowsViewer {
c := new(StructRowsViewer)
*c = *v
c.MapIndices = make(map[int]int, len(v.MapIndices))
for i, j := range v.MapIndices {
c.MapIndices[i] = j
}
return c
}
// String implements the fmt.Stringer interface for StructRowsViewer.
func (v *StructRowsViewer) String() string {
return fmt.Sprintf("StructRowsViewer{Tag: %q, Ignore: %q}", v.Tag, v.Ignore)
}
// NewView returns a View for a table made up of
// a slice or array of structs.
// NewView implements the Viewer interface for StructRowsViewer.
func (v *StructRowsViewer) NewView(title string, table any) (View, error) {
rows := reflect.ValueOf(table)
for rows.Kind() == reflect.Pointer && !rows.IsNil() {
rows = rows.Elem()
}
if rows.Kind() != reflect.Slice || rows.Kind() == reflect.Array {
return nil, fmt.Errorf("table must be slice or array kind but is %T", table)
}
rowType := rows.Type().Elem()
if rowType.Kind() == reflect.Pointer {
rowType = rowType.Elem()
}
if rowType.Kind() != reflect.Struct {
return nil, fmt.Errorf("row type must be a struct but is %s", rowType)
}
structFields := StructFieldTypes(rowType)
indices := make([]int, len(structFields))
columns := make([]string, 0, len(structFields))
columnIndexUsed := make(map[int]bool)
getNextFreeColumnIndex := func() int {
for i := range structFields {
if !columnIndexUsed[i] {
return i
}
}
panic("getNextFreeColumnIndex should always find a free column index")
}
for i, structField := range structFields {
column := v.StructFieldColumn(structField)
if column == v.Ignore {
indices[i] = -1
continue
}
index := getNextFreeColumnIndex()
if v.MapIndices != nil {
mappedIndex, ok := v.MapIndices[i]
if ok && !columnIndexUsed[mappedIndex] {
index = mappedIndex
}
}
if index < 0 || index >= len(structFields) {
indices[i] = -1
continue
}
indices[i] = index
columnIndexUsed[index] = true
columns = append(columns, column)
}
return NewStructRowsView(title, columns, indices, rows), nil
}
func (v *StructRowsViewer) WithTag(tag string) *StructRowsViewer {
mod := v.clone()
mod.Tag = tag
return mod
}
func (v *StructRowsViewer) WithIgnore(ignore string) *StructRowsViewer {
mod := v.clone()
mod.Ignore = ignore
return mod
}
func (v *StructRowsViewer) WithMapIndex(fieldIndex, columnIndex int) *StructRowsViewer {
mod := v.clone()
mod.MapIndices[fieldIndex] = columnIndex
return mod
}
func (v *StructRowsViewer) WithIgnoreFieldIndex(fieldIndex int) *StructRowsViewer {
mod := v.clone()
mod.MapIndices[fieldIndex] = -1
return mod
}
func (v *StructRowsViewer) WithIgnoreFieldIndices(fieldIndices ...int) *StructRowsViewer {
mod := v.clone()
for _, fieldIndex := range fieldIndices {
mod.MapIndices[fieldIndex] = -1
}
return mod
}
func (v *StructRowsViewer) WithIgnoreField(structPtr, fieldPtr any) *StructRowsViewer {
return v.WithIgnoreFieldIndex(MustStructFieldIndex(structPtr, fieldPtr))
}
func (v *StructRowsViewer) WithMapIndices(mapIndices map[int]int) *StructRowsViewer {
mod := v.clone()
mod.MapIndices = mapIndices
return mod
}