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
|
package formats
import (
"bytes"
"encoding/json"
"fmt"
"io"
"os"
"strings"
"text/tabwriter"
"text/template"
"golang.org/x/term"
"sigs.k8s.io/yaml"
)
const (
// JSONString const to save on duplicate variable names
JSONString = "json"
// IDString const to save on duplicates for Go templates
IDString = "{{.ID}}"
parsingErrorStr = "Template parsing error"
)
// Writer interface for outputs
type Writer interface {
Out() error
}
// JSONStructArray for JSON output
type JSONStructArray struct {
Output []interface{}
}
// StdoutTemplateArray for Go template output
type StdoutTemplateArray struct {
Output []interface{}
Template string
Fields map[string]string
}
// JSONStruct for JSON output
type JSONStruct struct {
Output interface{}
}
// StdoutTemplate for Go template output
type StdoutTemplate struct {
Output interface{}
Template string
Fields map[string]string
}
// YAMLStruct for YAML output
type YAMLStruct struct {
Output interface{}
}
func setJSONFormatEncoder(isTerminal bool, w io.Writer) *json.Encoder {
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
if isTerminal {
enc.SetEscapeHTML(false)
}
return enc
}
// Out method for JSON Arrays
func (j JSONStructArray) Out() error {
buf := bytes.NewBuffer(nil)
enc := setJSONFormatEncoder(term.IsTerminal(int(os.Stdout.Fd())), buf)
if err := enc.Encode(j.Output); err != nil {
return err
}
data := buf.Bytes()
// JSON returns a byte array with a literal null [110 117 108 108] in it
// if it is passed empty data. We used bytes.Compare to see if that is
// the case.
if diff := bytes.Compare(data, []byte("null")); diff == 0 {
data = []byte("[]")
}
// If the we did get NULL back, we should spit out {} which is
// at least valid JSON for the consumer.
fmt.Printf("%s", data)
humanNewLine()
return nil
}
// Out method for Go templates
func (t StdoutTemplateArray) Out() error {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
if strings.HasPrefix(t.Template, "table") {
// replace any spaces with tabs in template so that tabwriter can align it
t.Template = strings.Replace(strings.TrimSpace(t.Template[5:]), " ", "\t", -1)
headerTmpl, err := template.New("header").Funcs(headerFunctions).Parse(t.Template)
if err != nil {
return fmt.Errorf("%v: %w", parsingErrorStr, err)
}
err = headerTmpl.Execute(w, t.Fields)
if err != nil {
return err
}
fmt.Fprintln(w, "")
}
t.Template = strings.Replace(t.Template, " ", "\t", -1)
tmpl, err := template.New("image").Funcs(basicFunctions).Parse(t.Template)
if err != nil {
return fmt.Errorf("%v: %w", parsingErrorStr, err)
}
for _, raw := range t.Output {
basicTmpl := tmpl.Funcs(basicFunctions)
if err := basicTmpl.Execute(w, raw); err != nil {
return fmt.Errorf("%v: %w", parsingErrorStr, err)
}
fmt.Fprintln(w, "")
}
return w.Flush()
}
// Out method for JSON struct
func (j JSONStruct) Out() error {
data, err := json.MarshalIndent(j.Output, "", " ")
if err != nil {
return err
}
fmt.Printf("%s", data)
humanNewLine()
return nil
}
// Out method for Go templates
func (t StdoutTemplate) Out() error {
tmpl, err := template.New("image").Parse(t.Template)
if err != nil {
return fmt.Errorf("template parsing error: %w", err)
}
err = tmpl.Execute(os.Stdout, t.Output)
if err != nil {
return err
}
humanNewLine()
return nil
}
// Out method for YAML
func (y YAMLStruct) Out() error {
var buf []byte
var err error
buf, err = yaml.Marshal(y.Output)
if err != nil {
return err
}
fmt.Printf("%s", string(buf))
humanNewLine()
return nil
}
// humanNewLine prints a new line at the end of the output only if stdout is the terminal
func humanNewLine() {
if term.IsTerminal(int(os.Stdout.Fd())) {
fmt.Println()
}
}
|