databricks-cli/libs/cmdio/render.go

146 lines
3.4 KiB
Go

package cmdio
import (
"bytes"
"encoding/base64"
"encoding/json"
"io"
"strings"
"text/tabwriter"
"text/template"
"time"
"github.com/fatih/color"
"github.com/nwidger/jsoncolor"
)
// Heredoc is the equivalent of compute.TrimLeadingWhitespace
// (command-execution API helper from SDK), except it's more
// friendly to non-printable characters.
func Heredoc(tmpl string) (trimmed string) {
lines := strings.Split(tmpl, "\n")
leadingWhitespace := 1<<31 - 1
for _, line := range lines {
for pos, char := range line {
if char == ' ' || char == '\t' {
continue
}
// first non-whitespace character
if pos < leadingWhitespace {
leadingWhitespace = pos
}
// is not needed further
break
}
}
for i := 0; i < len(lines); i++ {
if lines[i] == "" || strings.TrimSpace(lines[i]) == "" {
continue
}
if len(lines[i]) < leadingWhitespace {
trimmed += lines[i] + "\n" // or not..
} else {
trimmed += lines[i][leadingWhitespace:] + "\n"
}
}
return strings.TrimSpace(trimmed)
}
func renderJson(w io.Writer, v any) error {
pretty, err := fancyJSON(v)
if err != nil {
return err
}
_, err = w.Write(pretty)
if err != nil {
return err
}
_, err = w.Write([]byte("\n"))
return err
}
func renderTemplate(w io.Writer, tmpl string, v any) error {
tw := tabwriter.NewWriter(w, 0, 4, 2, ' ', 0)
t, err := template.New("command").Funcs(template.FuncMap{
// we render colored output if stdout is TTY, otherwise we render text.
// in the future we'll check if we can explicitly check for stderr being
// a TTY
"header": color.BlueString,
"red": color.RedString,
"green": color.GreenString,
"blue": color.BlueString,
"yellow": color.YellowString,
"magenta": color.MagentaString,
"cyan": color.CyanString,
"replace": strings.ReplaceAll,
"join": strings.Join,
"bool": func(v bool) string {
if v {
return color.GreenString("YES")
}
return color.RedString("NO")
},
"pretty_json": func(in string) (string, error) {
var tmp any
err := json.Unmarshal([]byte(in), &tmp)
if err != nil {
return "", err
}
b, err := fancyJSON(tmp)
if err != nil {
return "", err
}
return string(b), nil
},
"pretty_date": func(t time.Time) string {
return t.Format("2006-01-02T15:04:05Z")
},
"b64_encode": func(in string) (string, error) {
var out bytes.Buffer
enc := base64.NewEncoder(base64.StdEncoding, &out)
_, err := enc.Write([]byte(in))
if err != nil {
return "", err
}
err = enc.Close()
if err != nil {
return "", err
}
return out.String(), nil
},
"b64_decode": func(in string) (string, error) {
dec := base64.NewDecoder(base64.StdEncoding, strings.NewReader(in))
out, err := io.ReadAll(dec)
if err != nil {
return "", err
}
return string(out), nil
},
}).Parse(tmpl)
if err != nil {
return err
}
err = t.Execute(tw, v)
if err != nil {
return err
}
return tw.Flush()
}
func fancyJSON(v any) ([]byte, error) {
// create custom formatter
f := jsoncolor.NewFormatter()
// set custom colors
f.StringColor = color.New(color.FgGreen)
f.TrueColor = color.New(color.FgGreen, color.Bold)
f.FalseColor = color.New(color.FgRed)
f.NumberColor = color.New(color.FgCyan)
f.NullColor = color.New(color.FgMagenta)
f.ObjectColor = color.New(color.Reset)
f.CommaColor = color.New(color.Reset)
f.ColonColor = color.New(color.Reset)
return jsoncolor.MarshalIndentWithFormatter(v, "", " ", f)
}