Repository navigation
Expand file tree
/
Copy pathindex_builder.go
More file actions
144 lines (135 loc) · 3.64 KB
/
Copy pathindex_builder.go
File metadata and controls
144 lines (135 loc) · 3.64 KB
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
141
142
143
144
package structexplorer
import (
"fmt"
"html/template"
"log/slog"
"runtime/debug"
"strconv"
"strings"
)
type indexDataBuilder struct {
data indexData
seq int
notLive bool
isBreaking bool // service is started with Break(...)
selectID string // id of the added fieldList (select element)
}
func newIndexDataBuilder() *indexDataBuilder {
b := new(indexDataBuilder)
b.data = indexData{
Script: template.JS(scriptJS),
Style: template.CSS(styleCSS),
}
return b
}
type cellInfo struct {
entriesCount int
hasZeros bool
}
func (b *indexDataBuilder) build(row, column int, access objectAccess) cellInfo {
// check room
for len(b.data.Rows) <= row {
b.data.Rows = append(b.data.Rows, tableRow{})
}
for len(b.data.Rows[row].Cells) <= column {
b.data.Rows[row].Cells = append(b.data.Rows[row].Cells, fieldList{})
}
// copy fields into entries
hasZeros := false
entries := []fieldEntry{}
currentValue := access.Value()
for _, each := range newFields(currentValue) {
valString := safeComputeValueString(each)
if isZeroPrintstring(valString) {
hasZeros = true
if access.hideZeros {
continue
}
}
label := each.displayKey()
entryKey := each.key
// if the access is part of a large slice or array
// then offset both the key and label
if access.sliceRange.size() > 1 {
ik, _ := strconv.Atoi(entryKey)
label = strconv.Itoa(ik + access.sliceRange.from)
entryKey = label
}
entries = append(entries, fieldEntry{
Label: label,
Key: entryKey,
Type: each.Type,
ValueString: valString,
})
}
entries = applyFieldNamePadding(entries)
size := computeSizeOfWidestEntry(entries)
// adjust label so that table cell width is used to display select options
fieldListLabel := access.label
if size > len(fieldListLabel) {
fieldListLabel += strings.Repeat(" ", size-len(access.label))
}
newSelectID := fmt.Sprintf("id%d", b.seq)
typ := access.typeName
if typ == "" {
// when using Follow, the type is not set/known
typ = fmt.Sprintf("%T", currentValue)
}
b.data.Rows[row].Cells[column] = fieldList{
Row: row,
Column: column,
Path: strings.Join(access.path, "."),
Label: template.HTML(fieldListLabel),
Fields: entries,
Type: typ,
IsRoot: access.isRoot,
HasZeros: hasZeros,
SelectSize: len(entries),
Multiple: multiple(len(entries)),
SelectID: newSelectID,
NotLive: b.notLive,
}
b.selectID = newSelectID
b.seq++
return cellInfo{entriesCount: len(entries), hasZeros: hasZeros}
}
// due to change in Chrome, when need to leave out the attribute is size <= 1
func multiple(size int) string {
if size > 1 {
return "multiple"
}
return ""
}
func safeComputeValueString(fa fieldAccess) string {
if s, ok := tryComputeValueString(fa); ok {
return ellipsis(s)
}
return ellipsis(fallbackPrintString(fa.value()))
}
func tryComputeValueString(fa fieldAccess) (string, bool) {
// capture panics
defer func() {
if err := recover(); err != nil {
slog.Error("[structexplorer] failed to get value of entry, fallback display",
"field", fa.key, "field.label", fa.label,
"field.type", fa.Type, "owner.type", fmt.Sprintf("%T", fa.owner),
"err", err)
full := string(debug.Stack())
methodToken := "structexplorer.printString"
idx := strings.Index(full, methodToken)
fmt.Println(full[:idx+len(methodToken)], "... (more stack left out)")
return
}
}()
return ellipsis(printString(fa.value())), true
}
func computeSizeOfWidestEntry(list []fieldEntry) int {
size := 0
for _, each := range list {
s := len(each.Label) + len(": ") + len(each.ValueString)
if s > size {
size = s
}
}
return size
}