/
output_filter.go
152 lines (133 loc) · 3.62 KB
/
output_filter.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
/*
* Copyright (C) 1999-2019 Alibaba Group Holding Limited
*/
package openapi
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"strings"
"text/tabwriter"
"github.com/aliyun/aliyun-cli/cli"
"github.com/aliyun/aliyun-cli/i18n"
jmespath "github.com/jmespath/go-jmespath"
)
func NewOutputFlag() *cli.Flag {
return &cli.Flag{
Name: OutputFlagName,
Shorthand: 'o',
AssignedMode: cli.AssignedRepeatable,
Short: i18n.T(
"use `--output cols=Field1,Field2 [rows=jmesPath]` to print output as table",
"使用 `--output cols=Field1,Field1 [rows=jmesPath]` 使用表格方式打印输出",
),
Long: i18n.T(
"",
"",
),
Fields: []cli.Field{
{Key: "cols", Repeatable: false, Required: true},
{Key: "rows", Repeatable: false, Required: false},
{Key: "num", Repeatable: false, Required: false},
},
}
}
type OutputFilter interface {
FilterOutput(input string) (string, error)
}
func GetOutputFilter(ctx *cli.Context) OutputFilter {
if !OutputFlag(ctx.Flags()).IsAssigned() {
return nil
}
return NewTableOutputFilter(ctx)
}
type TableOutputFilter struct {
ctx *cli.Context
}
func NewTableOutputFilter(ctx *cli.Context) OutputFilter {
return &TableOutputFilter{ctx: ctx}
}
func (a *TableOutputFilter) FilterOutput(s string) (string, error) {
var v interface{}
s = fmt.Sprintf("{\"RootFilter\":[%s]}", s)
err := json.Unmarshal([]byte(s), &v)
if err != nil {
return s, fmt.Errorf("unmarshal output failed %s", err)
}
rowPath := detectArrayPath(v)
if v, ok := OutputFlag(a.ctx.Flags()).GetFieldValue("rows"); ok {
rowPath = "RootFilter[0]." + v
} else {
rowPath = "RootFilter"
}
var colNames []string
if v, ok := OutputFlag(a.ctx.Flags()).GetFieldValue("cols"); ok {
v = cli.UnquoteString(v)
colNames = strings.Split(v, ",")
} else {
return s, fmt.Errorf("you need to assign col=col1,col2,... with --output")
}
return a.FormatTable(rowPath, colNames, v)
}
func (a *TableOutputFilter) FormatTable(rowPath string, colNames []string, v interface{}) (string, error) {
// Add row number
if v, ok := OutputFlag(a.ctx.Flags()).GetFieldValue("num"); ok {
if v == "true" {
colNames = append([]string{"Num"}, colNames...)
}
}
rows, err := jmespath.Search(rowPath, v)
if err != nil {
return "", fmt.Errorf("jmespath: '%s' failed %s", rowPath, err)
}
rowsArray, ok := rows.([]interface{})
if !ok {
return "", fmt.Errorf("jmespath: '%s' failed Need Array Expr", rowPath)
}
var buf bytes.Buffer
writer := bufio.NewWriter(&buf)
format := strings.Repeat("%v\t ", len(colNames)-1) + "%v"
w := tabwriter.NewWriter(writer, 0, 0, 1, ' ', tabwriter.Debug)
fmt.Fprintln(w, fmt.Sprintf(format, toIntfArray(colNames)...))
separator := ""
for i, colName := range colNames {
separator = separator + strings.Repeat("-", len(colName))
if i < len(colNames)-1 {
separator = separator + "\t "
}
}
fmt.Fprintln(w, separator)
for i, row := range rowsArray {
rowIntf, ok := row.(interface{})
if !ok {
// fmt.Errorf("parse row to interface failed")
}
r := make([]string, 0)
var s string
var index int
if v, ok := OutputFlag(a.ctx.Flags()).GetFieldValue("num"); ok {
if v == "true" {
s = fmt.Sprintf("%v", i)
r = append(r, s)
index = 1
}
}
for _, colName := range colNames[index:] {
v, _ := jmespath.Search(colName, rowIntf)
s = fmt.Sprintf("%v", v)
r = append(r, s)
}
fmt.Fprintln(w, fmt.Sprintf(format, toIntfArray(r)...))
}
w.Flush()
writer.Flush()
return buf.String(), nil
}
func toIntfArray(stringArray []string) []interface{} {
intfArray := []interface{}{}
for _, elem := range stringArray {
intfArray = append(intfArray, elem)
}
return intfArray
}