-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathteks.go
144 lines (127 loc) · 3.96 KB
/
teks.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
package teks
import (
"bytes"
"encoding/json"
"fmt"
"io"
"strings"
"text/tabwriter"
"text/template"
)
// TableFormatKey is the identifier used for table
const TableFormatKey = "table"
// basicFuncs are used for common data printing
var basicFuncs = template.FuncMap{
"json": func(v interface{}) string {
buf := &bytes.Buffer{}
encoder := json.NewEncoder(buf)
encoder.SetEscapeHTML(false)
_ = encoder.Encode(v)
return strings.TrimSpace(buf.String())
},
"jsonPretty": func(v interface{}) string {
buf := &bytes.Buffer{}
encoder := json.NewEncoder(buf)
encoder.SetEscapeHTML(false)
encoder.SetIndent("", " ")
_ = encoder.Encode(v)
return strings.TrimSpace(buf.String())
},
"split": strings.Split,
"upper": strings.ToUpper,
"lower": strings.ToLower,
"title": strings.Title,
"join": strings.Join,
}
// HeaderFuncs are used to format headers in a table
// Some of functions in basicFuncs are overridden
var HeaderFuncs = template.FuncMap{
"json": func(s string) string { return s },
"jsonPretty": func(s string) string { return s },
"join": func(s string) string { return s },
}
// Format is an alias for a string used for formatting
type Format string
// IsTable returns true if format string is prefixed with table
func (f Format) IsTable() bool {
return strings.HasPrefix(string(f), TableFormatKey)
}
// Context keeps data about a format operation
type Context struct {
// Output is used to write the output
Output io.Writer
// Format is used to keep format string
Format Format
// internal usage
finalFormat string
buffer *bytes.Buffer
}
// NewContext creates a context with initialized fields
func NewContext(output io.Writer, format string) *Context {
return &Context{Output: output, Format: Format(format), buffer: &bytes.Buffer{}}
}
// preFormat will clean format string
func (ctx *Context) preFormat() {
format := string(ctx.Format)
if ctx.Format.IsTable() {
// if table is found skip it and take the rest
format = format[len(TableFormatKey):]
}
format = strings.TrimSpace(format)
// this is done to avoid treating \t \n as template strings. This replaces them as special characters
replacer := strings.NewReplacer(`\t`, "\t", `\n`, "\n")
format = replacer.Replace(format)
ctx.finalFormat = format
}
// parseTemplate will create a new template with basic functions
func (ctx Context) parseTemplate() (*template.Template, error) {
tmpl, err := NewBasicFormatter("").Parse(ctx.finalFormat)
if err != nil {
return tmpl, fmt.Errorf("Template parsing error: %v\n", err)
}
return tmpl, nil
}
// postFormat will output to writer
func (ctx *Context) postFormat(template *template.Template, headers interface{}) {
if ctx.Format.IsTable() {
// create a tab writer using Output
w := tabwriter.NewWriter(ctx.Output, 20, 1, 3, ' ', 0)
// print headers
_ = template.Funcs(HeaderFuncs).Execute(w, headers)
_, _ = w.Write([]byte{'\n'})
// write buffer to the w
// in this case anything in buffer will be rendered by tab writer to the Output
// buffer contains data to be written
_, _ = ctx.buffer.WriteTo(w)
// flush will perform actual write to the writer
_ = w.Flush()
} else {
// just write it as normal
_, _ = ctx.buffer.WriteTo(ctx.Output)
}
}
// Renderer is used to render a particular resource using templates
type Renderer func(io.Writer, *template.Template) error
// Write writes data using r and headers
func (ctx *Context) Write(r Renderer, headers interface{}) error {
// prepare formatting
ctx.preFormat()
// parse template
tmpl, err := ctx.parseTemplate()
if err != nil {
return err
}
// using renderer provided render collection
// Note: See the renderer implementation in cmd/apis.go for more
if err = r(ctx.buffer, tmpl); err != nil {
return err
}
// write results to writer
ctx.postFormat(tmpl, headers)
return nil
}
// NewBasicFormatter creates a new template engine with name
func NewBasicFormatter(name string) *template.Template {
tmpl := template.New(name).Funcs(basicFuncs)
return tmpl
}