/
format_utils.go
209 lines (180 loc) · 5.01 KB
/
format_utils.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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
package cmdlib
import (
"encoding/json"
"fmt"
"reflect"
"strings"
)
// defaultListIndent - default indent level for lists
const defaultIndent = " "
// Converts to generic interface that jsonpath can work with
func toGenericObject(v interface{}) (interface{}, error) {
var jsonData interface{}
data, err := json.Marshal(v)
if err != nil {
return nil, fmt.Errorf("unable to marshal. %v", err)
}
err = json.Unmarshal(data, &jsonData)
if err != nil {
return nil, fmt.Errorf("unable to unmarshal. %v", err)
}
return jsonData, nil
}
// getFieldValue returns value of field in an object through reflection
func getFieldValue(v interface{}, fieldPath string) (interface{}, error) {
for _, fieldName := range strings.Split(fieldPath, ".") {
val := reflect.ValueOf(v)
if v == nil || val.IsNil() {
return nil, nil
}
if val.Type().Kind() == reflect.Ptr {
val = val.Elem()
}
if val.Type().Kind() != reflect.Struct {
return nil, fmt.Errorf("invalid type %s for path %s", val.Type().String(), fieldName)
}
var found bool
for i := 0; i < val.Type().NumField(); i++ {
if val.Type().Field(i).Name == fieldName {
val = val.Field(i)
v = val.Interface()
found = true
break
}
}
if !found {
return nil, fmt.Errorf("unable to find field %s", fieldName)
}
}
return v, nil
}
// computeMaxColumnSize computes the max column size
func computeMaxColumnSize(o Output, includeHeader bool) []int {
colMaxSize := make([]int, len(o.Columns))
for _, r := range o.Rows {
for i, c := range r.Values {
if len(c) > colMaxSize[i] {
colMaxSize[i] = len(c)
}
if includeHeader && len(o.Columns[i]) > colMaxSize[i] {
colMaxSize[i] = len(o.Columns[i])
}
}
}
return colMaxSize
}
// printSingleRow prints single row from given column values.
func printSingleRow(values []string, maxColSize []int, fo FormatOptions) string {
var result string
// write column headers
for i, c := range values {
result += c
if i < len(values)-1 {
result += strings.Repeat(" ", maxColSize[i]-len(c)+1)
}
}
return result
}
// printList converts structured input into simple list of key/value pairs
func printList(out Output, fo FormatOptions, indent string) ([]string, error) {
if len(out.Rows) == 0 {
return nil, nil
}
var result []string
if !fo.NoHeader {
result = append(result, fmt.Sprintf("%s%s:", indent, out.Title))
}
indent += defaultIndent
// Compute the header value that has the longest size.
var maxHeaderLength int
for _, c := range out.Columns {
if len(c) > maxHeaderLength {
maxHeaderLength = len(c)
}
}
for _, r := range out.Rows {
for i, v := range r.Values {
reqIndent := strings.Repeat(" ", maxHeaderLength-len(out.Columns[i])+1)
result = append(result, fmt.Sprintf("%s%s:%s%s", indent, out.Columns[i], reqIndent, v))
}
for _, child := range r.Children {
childResult, err := printList(child, fo, indent)
if err != nil {
return nil, err
}
result = append(result, childResult...)
}
result = append(result, "")
}
return result, nil
}
// printColumns converts structured input into set of simple lines that is ready for printing.
func printColumns(out Output, fo FormatOptions, indent string) ([]string, error) {
if len(out.Rows) == 0 {
return nil, nil
}
var colMaxSize []int
colMaxSize = computeMaxColumnSize(out, !fo.NoHeader)
var rawRows []string
if !fo.NoHeader {
rawRows = append(rawRows, indent+printSingleRow(out.Columns, colMaxSize, fo))
}
for rowIndex, r := range out.Rows {
row := printSingleRow(r.Values, colMaxSize, fo)
rawRows = append(rawRows, indent+row)
// Process sub items
if len(r.Children) > 0 && fo.Type == FormatTypeTabular {
for _, so := range r.Children {
subItemIndent := indent + " "
// Print sub item title
rawRows = append(rawRows, "")
rawRows = append(rawRows, subItemIndent+"@"+so.Title+":")
// Generate the rows
subRows, err := printColumns(so, fo, subItemIndent)
if err != nil {
return nil, err
}
rawRows = append(rawRows, subRows...)
}
// Reprint the header if there are sub items.
if !fo.NoHeader && rowIndex < len(out.Rows)-1 {
rawRows = append(rawRows, "")
rawRows = append(rawRows, indent+printSingleRow(out.Columns, colMaxSize, fo))
}
}
}
return rawRows, nil
}
// getFormatter returns formatter for the given type.
// If formatter is not registered, it will return the default formatter.
func getFormatter(log Logger, v interface{}) Formatter {
tp := reflect.TypeOf(v)
if tp.Kind() == reflect.Slice {
tp = tp.Elem()
}
f, ok := formatRegistry[tp]
if ok {
return f
}
log.Warningf("formatter for type %s not found, returning default", reflect.TypeOf(v))
return Formatter{
CustomFn: func(v interface{}, fo FormatOptions) Output {
contents := MarshalJSON(v)
var rows []OutputRow
for _, r := range strings.Split(contents, "\n") {
rows = append(rows, OutputRow{
Values: []string{r},
})
}
return Output{
Columns: []string{"RAW"},
Rows: rows,
}
},
Columns: []Column{
{
Name: "RAW",
},
},
}
}