-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmktpl.go
191 lines (146 loc) · 4.15 KB
/
mktpl.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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
package main
import (
"bytes"
"errors"
"flag"
"fmt"
"io"
"os"
"regexp"
"text/template"
"github.com/Masterminds/sprig/v3"
"gopkg.in/yaml.v2"
)
const helpText = `mktpl is a tool to render Golang text/template with template and YAML data files.
Usage:
mktpl flags
Flags (* is mandatory):
-d, --data string path to the YAML data file (*)
-t, --template string path to the template file (*)
-h, --help help for mktpl
-v, --version show program's version information and exit`
const (
exitCodeOK int = 0
// Errors start from 11.
exitCodeError = 10 + iota
exitCodeParseFlagsError
exitCodeInvalidFlags
exitCodeInvalidFilePath
exitCodeParseTemplateError
)
// Flags.
var (
tplPath string
dataPath string
showHelp bool
showVersion bool
)
// version information.
var (
// These values are embedded when building.
buildVersion string
buildRevision string
buildWith string
)
var errMissingFlags = errors.New("omitting -d|--data and -t|--template flags is not allowed")
var regex = regexp.MustCompile(`{{\s*-?\s*(\.?\w+\s*)+-?\s*}}`)
type mktpl struct {
outStream, errStream io.Writer
}
func (m *mktpl) parseFlags(args []string) error {
flags := flag.NewFlagSet(args[0], flag.ExitOnError)
flags.SetOutput(m.errStream)
flags.Usage = func() {
fmt.Fprintf(m.errStream, "%s\n", helpText)
}
flags.StringVar(&dataPath, "d", "", "")
flags.StringVar(&dataPath, "data", "", "")
flags.StringVar(&tplPath, "t", "", "")
flags.StringVar(&tplPath, "template", "", "")
flags.BoolVar(&showHelp, "h", false, "")
flags.BoolVar(&showHelp, "help", false, "")
flags.BoolVar(&showVersion, "v", false, "")
flags.BoolVar(&showVersion, "version", false, "")
// Parse flag
if err := flags.Parse(args[1:]); err != nil {
return fmt.Errorf("%w", err)
}
return nil
}
// Run is the actual main function.
func (m *mktpl) Run(args []string) int {
if err := m.parseFlags(args); err != nil {
fmt.Fprintf(m.errStream, "faild in parsing flags: %s\n", err)
return exitCodeParseFlagsError
}
if showHelp {
fmt.Fprintf(m.outStream, "%s\n", helpText)
return exitCodeOK
}
if showVersion {
fmt.Fprintf(m.outStream, "version: %s\nrevision: %s\nwith: %s\n",
buildVersion, buildRevision, buildWith)
return exitCodeOK
}
if err := isValidFlags(); err != nil {
fmt.Fprintf(m.errStream, "invalid flags: %s\n", err)
return exitCodeInvalidFlags
}
data, err := os.ReadFile(dataPath)
if err != nil {
fmt.Fprintf(m.errStream, "failed in reading the data file: %s\n", err)
return exitCodeInvalidFilePath
}
text, err := os.ReadFile(tplPath)
if err != nil {
fmt.Fprintf(m.errStream, "failed in reading the template file: %s\n", err)
return exitCodeInvalidFilePath
}
tpl, err := parseTemplate(string(text))
if err != nil {
fmt.Fprintf(m.errStream, "failed in parsing the template file: %s\n", err)
return exitCodeParseTemplateError
}
var out []byte
if out, err = render(data, tpl); err != nil {
fmt.Fprintf(m.errStream, "%s\n", err)
return exitCodeError
}
fmt.Fprintf(m.outStream, "%s", string(out))
return exitCodeOK
}
func parseTemplate(text string) (*template.Template, error) {
tpl, err := template.New("").Funcs(mergeTemplateFuncMaps(tplFuncMap, sprig.FuncMap())).Parse(text)
if err != nil {
return nil, fmt.Errorf("%w", err)
}
return tpl, nil
}
func isValidFlags() error {
if len(tplPath) == 0 || len(dataPath) == 0 {
return fmt.Errorf("%w", errMissingFlags)
}
return nil
}
func render(data []byte, tpl *template.Template) ([]byte, error) {
mappedData := make(map[interface{}]interface{})
if err := yaml.Unmarshal(data, &mappedData); err != nil {
return nil, fmt.Errorf("failed in unmarshalling the YAML data: %w", err)
}
buf := new(bytes.Buffer)
if err := tpl.Execute(buf, mappedData); err != nil {
return nil, fmt.Errorf("failed in rendering: %w", err)
}
out, err := io.ReadAll(buf)
if err != nil {
return nil, fmt.Errorf("failed in reading the buffered text: %w", err)
}
if regex.Match(out) {
tpl, err := parseTemplate(string(out))
if err != nil {
return nil, fmt.Errorf("failed in parsing the buffered template %w", err)
}
return render(data, tpl)
}
return out, nil
}