|
| 1 | +package pkg |
| 2 | + |
| 3 | +import ( |
| 4 | + "encoding/json" |
| 5 | + "fmt" |
| 6 | + "github.com/spf13/cobra" |
| 7 | + "gopkg.in/yaml.v2" |
| 8 | + "io" |
| 9 | + "reflect" |
| 10 | + "strings" |
| 11 | +) |
| 12 | + |
| 13 | +// OutputOption represent the format of output |
| 14 | +type OutputOption struct { |
| 15 | + Format string |
| 16 | + |
| 17 | + Columns string |
| 18 | + WithoutHeaders bool |
| 19 | + Filter []string |
| 20 | + |
| 21 | + Writer io.Writer |
| 22 | + CellRenderMap map[string]RenderCell |
| 23 | +} |
| 24 | + |
| 25 | +// RenderCell render a specific cell in a table |
| 26 | +type RenderCell = func(string) string |
| 27 | + |
| 28 | +// FormatOutput is the interface of format output |
| 29 | +type FormatOutput interface { |
| 30 | + Output(obj interface{}, format string) (data []byte, err error) |
| 31 | +} |
| 32 | + |
| 33 | +const ( |
| 34 | + // JSONOutputFormat is the format of json |
| 35 | + JSONOutputFormat string = "json" |
| 36 | + // YAMLOutputFormat is the format of yaml |
| 37 | + YAMLOutputFormat string = "yaml" |
| 38 | + // TableOutputFormat is the format of table |
| 39 | + TableOutputFormat string = "table" |
| 40 | +) |
| 41 | + |
| 42 | +// Output print the object into byte array |
| 43 | +// Deprecated see also OutputV2 |
| 44 | +func (o *OutputOption) Output(obj interface{}) (data []byte, err error) { |
| 45 | + switch o.Format { |
| 46 | + case JSONOutputFormat: |
| 47 | + return json.MarshalIndent(obj, "", " ") |
| 48 | + case YAMLOutputFormat: |
| 49 | + return yaml.Marshal(obj) |
| 50 | + } |
| 51 | + |
| 52 | + return nil, fmt.Errorf("not support format %s", o.Format) |
| 53 | +} |
| 54 | + |
| 55 | +// OutputV2 print the data line by line |
| 56 | +func (o *OutputOption) OutputV2(obj interface{}) (err error) { |
| 57 | + if o.Writer == nil { |
| 58 | + err = fmt.Errorf("no writer found") |
| 59 | + return |
| 60 | + } |
| 61 | + |
| 62 | + if len(o.Columns) == 0 { |
| 63 | + err = fmt.Errorf("no columns found") |
| 64 | + return |
| 65 | + } |
| 66 | + |
| 67 | + //cmd.logger.Debug("start to output", zap.Any("filter", o.Filter)) |
| 68 | + obj = o.ListFilter(obj) |
| 69 | + |
| 70 | + var data []byte |
| 71 | + switch o.Format { |
| 72 | + case JSONOutputFormat: |
| 73 | + data, err = json.MarshalIndent(obj, "", " ") |
| 74 | + case YAMLOutputFormat: |
| 75 | + data, err = yaml.Marshal(obj) |
| 76 | + case TableOutputFormat, "": |
| 77 | + table := CreateTableWithHeader(o.Writer, o.WithoutHeaders) |
| 78 | + table.AddHeader(strings.Split(o.Columns, ",")...) |
| 79 | + items := reflect.ValueOf(obj) |
| 80 | + for i := 0; i < items.Len(); i++ { |
| 81 | + table.AddRow(o.GetLine(items.Index(i))...) |
| 82 | + } |
| 83 | + table.Render() |
| 84 | + default: |
| 85 | + err = fmt.Errorf("not support format %s", o.Format) |
| 86 | + } |
| 87 | + |
| 88 | + if err == nil && len(data) > 0 { |
| 89 | + _, err = o.Writer.Write(data) |
| 90 | + } |
| 91 | + return |
| 92 | +} |
| 93 | + |
| 94 | +// ListFilter filter the data list by fields |
| 95 | +func (o *OutputOption) ListFilter(obj interface{}) interface{} { |
| 96 | + if len(o.Filter) == 0 { |
| 97 | + return obj |
| 98 | + } |
| 99 | + |
| 100 | + elemType := reflect.TypeOf(obj).Elem() |
| 101 | + elemSlice := reflect.MakeSlice(reflect.SliceOf(elemType), 0, 10) |
| 102 | + items := reflect.ValueOf(obj) |
| 103 | + for i := 0; i < items.Len(); i++ { |
| 104 | + item := items.Index(i) |
| 105 | + if o.Match(item) { |
| 106 | + elemSlice = reflect.Append(elemSlice, item) |
| 107 | + } |
| 108 | + } |
| 109 | + return elemSlice.Interface() |
| 110 | +} |
| 111 | + |
| 112 | +// Match filter an item |
| 113 | +func (o *OutputOption) Match(item reflect.Value) bool { |
| 114 | + for _, f := range o.Filter { |
| 115 | + arr := strings.Split(f, "=") |
| 116 | + if len(arr) < 2 { |
| 117 | + continue |
| 118 | + } |
| 119 | + |
| 120 | + key := arr[0] |
| 121 | + val := arr[1] |
| 122 | + |
| 123 | + if !strings.Contains(ReflectFieldValueAsString(item, key), val) { |
| 124 | + return false |
| 125 | + } |
| 126 | + } |
| 127 | + return true |
| 128 | +} |
| 129 | + |
| 130 | +// GetLine returns the line of a table |
| 131 | +func (o *OutputOption) GetLine(obj reflect.Value) []string { |
| 132 | + columns := strings.Split(o.Columns, ",") |
| 133 | + values := make([]string, 0) |
| 134 | + |
| 135 | + if o.CellRenderMap == nil { |
| 136 | + o.CellRenderMap = make(map[string]RenderCell, 0) |
| 137 | + } |
| 138 | + |
| 139 | + for _, col := range columns { |
| 140 | + cell := ReflectFieldValueAsString(obj, col) |
| 141 | + if renderCell, ok := o.CellRenderMap[col]; ok && renderCell != nil { |
| 142 | + cell = renderCell(cell) |
| 143 | + } |
| 144 | + |
| 145 | + values = append(values, cell) |
| 146 | + } |
| 147 | + return values |
| 148 | +} |
| 149 | + |
| 150 | +// SetFlag set flag of output format |
| 151 | +// Deprecated, see also SetFlagWithHeaders |
| 152 | +func (o *OutputOption) SetFlag(cmd *cobra.Command) { |
| 153 | + cmd.Flags().StringVarP(&o.Format, "output", "o", TableOutputFormat, |
| 154 | + "Format the output, supported formats: table, json, yaml") |
| 155 | + cmd.Flags().BoolVarP(&o.WithoutHeaders, "no-headers", "", false, |
| 156 | + `When using the default output format, don't print headers (default print headers)`) |
| 157 | + cmd.Flags().StringArrayVarP(&o.Filter, "filter", "", []string{}, |
| 158 | + "Filter for the list by fields") |
| 159 | +} |
| 160 | + |
| 161 | +// SetFlagWithHeaders set the flags of output |
| 162 | +func (o *OutputOption) SetFlagWithHeaders(cmd *cobra.Command, headers string) { |
| 163 | + o.SetFlag(cmd) |
| 164 | + cmd.Flags().StringVarP(&o.Columns, "columns", "", headers, |
| 165 | + "The columns of table") |
| 166 | +} |
0 commit comments