-
Notifications
You must be signed in to change notification settings - Fork 10
/
formatter_template.go
108 lines (89 loc) · 2.94 KB
/
formatter_template.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
package controldisplay
import (
"context"
"fmt"
"io"
"os"
"text/template"
"github.com/turbot/pipe-fittings/app_specific"
"github.com/spf13/viper"
"github.com/turbot/pipe-fittings/constants"
"github.com/turbot/pipe-fittings/utils"
"github.com/turbot/powerpipe/internal/controlexecute"
)
// TemplateFormatter implements the 'Formatter' interface and exposes a generic template based output mechanism
// for 'check' execution trees
type TemplateFormatter struct {
template *template.Template
exportFormat *OutputTemplate
}
func NewTemplateFormatter(input *OutputTemplate) (*TemplateFormatter, error) {
templateFuncs := templateFuncs(TemplateRenderContext{})
// add a stub "render_context" function
// this will be overwritten before we execute the template
// if we don't put this here, then templates which use this
// won't parse and will throw Error: template: ****: function "render_context" not defined
templateFuncs["render_context"] = func() TemplateRenderContext { return TemplateRenderContext{} }
t, err := template.New("outlet").
Funcs(templateFuncs).
ParseFS(os.DirFS(input.TemplatePath), "*")
if err != nil {
return nil, fmt.Errorf("could not load template '%s' - %v", input.TemplatePath, err)
}
return &TemplateFormatter{exportFormat: input, template: t}, nil
}
func (tf TemplateFormatter) Format(ctx context.Context, tree *controlexecute.ExecutionTree) (io.Reader, error) {
reader, writer := io.Pipe()
go func() {
workingDirectory, err := os.Getwd()
if err != nil {
writer.CloseWithError(err)
return
}
renderContext := TemplateRenderContext{
Constants: TemplateRenderConstants{
PowerpipeVersion: app_specific.AppVersion.String(),
WorkingDir: workingDirectory,
},
Config: TemplateRenderConfig{
RenderHeader: viper.GetBool(constants.ArgHeader),
Separator: viper.GetString(constants.ArgSeparator),
},
Data: tree,
}
// overwrite the "render_context" function to return the current render context
templateFuncs := templateFuncs(renderContext)
templateFuncs["render_context"] = func() TemplateRenderContext { return renderContext }
t, err := tf.template.Clone()
if err != nil {
writer.CloseWithError(err)
return
}
t = t.Funcs(templateFuncs)
if err := t.ExecuteTemplate(writer, "output", renderContext); err != nil {
writer.CloseWithError(err)
} else {
writer.Close()
}
}()
// tactical - for json, prettify the output
if tf.shouldPrettify() {
return utils.PrettifyJsonFromReader(reader)
}
return reader, nil
}
func (tf TemplateFormatter) FileExtension() string {
return tf.exportFormat.FileExtension
}
func (tf TemplateFormatter) Name() string {
return tf.exportFormat.FormatName
}
func (tf TemplateFormatter) Alias() string {
if tf.exportFormat.FormatFullName != tf.exportFormat.FormatName {
return tf.exportFormat.FormatFullName
}
return ""
}
func (tf TemplateFormatter) shouldPrettify() bool {
return tf.Name() == constants.OutputFormatJSON
}