-
Notifications
You must be signed in to change notification settings - Fork 1
/
generate.go
245 lines (213 loc) · 5.97 KB
/
generate.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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
package cmdconfig
import (
"bytes"
"fmt"
"path/filepath"
"regexp"
"strings"
"text/template"
"unicode"
"github.com/pkg/errors"
)
func KeyPrefixTemplate(prefix string) string {
return fmt.Sprintf("%sTEMPLATE", prefix)
}
func KeyPrefixExtensions(prefix string) string {
return fmt.Sprintf("%sX", prefix)
}
func KeyExtensionsDir(prefix string) string {
return fmt.Sprintf("%sX_DIR", prefix)
}
type GenerateKey struct {
KeyPrefix string
KeyPrivate string
Key string
}
type TemplateParam struct {
KeyPrivate string
Key string
// Implicit is set if the param is also a config key
Implicit bool
}
// TemplateKey, e.g. APP_TEMPLATE_*
type TemplateKey struct {
GenerateKey
ExplicitParams string
Params []TemplateParam
}
type GenerateData struct {
Prefix string
AppDir string
Keys []GenerateKey
TemplateKeys []TemplateKey
// KeyMap can be used to lookup an index in Keys given a key
KeyMap map[string]int
}
func NewGenerateData(in *CmdIn) (data *GenerateData, err error) {
// Init
data = &GenerateData{
Prefix: in.Prefix,
AppDir: in.AppDir,
}
_, config, err := newConf(confParams{
appDir: in.AppDir,
env: in.Env,
extend: in.Extend,
merge: in.Merge,
})
if err != nil {
return data, err
}
// APP_DIR is usually not set in the config.json file
keys := make([]string, len(config.Keys))
copy(keys, config.Keys)
keys = append(keys, fmt.Sprintf("%vDIR", in.Prefix))
data.Keys = make([]GenerateKey, len(keys))
data.TemplateKeys = make([]TemplateKey, 0)
data.KeyMap = make(map[string]int)
configFileKeys := make(map[string]bool)
templateKeys := make([]GenerateKey, 0)
// Prepare data for generating config helper files
for i, keyWithPrefix := range keys {
formattedKey := FormatKey(in.Prefix, keyWithPrefix)
configFileKeys[formattedKey] = true
generateKey := GenerateKey{
KeyPrefix: keyWithPrefix,
KeyPrivate: ToPrivate(formattedKey),
Key: formattedKey,
}
data.Keys[i] = generateKey
data.KeyMap[formattedKey] = i
// If template key then append to templateKeys
if strings.HasPrefix(keyWithPrefix, KeyPrefixTemplate(in.Prefix)) {
templateKeys = append(templateKeys, generateKey)
}
}
// Template keys are use to generate template.go
for _, generateKey := range templateKeys {
templateKey := TemplateKey{
GenerateKey: generateKey,
}
params := GetTemplateParams(config.Map[generateKey.KeyPrefix])
explicitParams := make([]string, 0)
for _, param := range params {
keyPrivate := ToPrivate(param)
implicit := false
if _, ok := configFileKeys[param]; ok {
implicit = true
} else {
explicitParams = append(explicitParams, keyPrivate)
}
templateKey.Params = append(
templateKey.Params, TemplateParam{
KeyPrivate: keyPrivate,
Key: param,
Implicit: implicit,
})
}
if len(explicitParams) > 0 {
templateKey.ExplicitParams =
strings.Join(explicitParams, ", ") + " string"
}
data.TemplateKeys = append(data.TemplateKeys, templateKey)
}
return data, nil
}
// GetTemplateParams from template, e.g.
// passing in "Fizz{{.Buz}}{{.Meh}}" should return ["Buz", "Meh"]
func GetTemplateParams(value string) (params []string) {
params = make([]string, 0)
// Replace all mustache params with substring groups match
s := "\\{\\{\\.(\\w*)}}"
r, err := regexp.Compile(s)
if err != nil {
return params
}
matches := r.FindAllStringSubmatch(value, -1)
for _, match := range matches {
params = append(params, match[1])
}
return params
}
// FormatKey removes the prefix and converts env var to golang var,
// e.g. APP_FOO_BAR becomes FooBar
func FormatKey(prefix, keyWithPrefix string) string {
key := strings.Replace(keyWithPrefix, prefix, "", 1)
key = strings.Replace(key, "_", " ", -1)
key = strings.ToLower(key)
key = strings.Replace(strings.Title(key), " ", "", -1)
return key
}
// ToPrivate lowercases the first character of str
func ToPrivate(str string) string {
for i, v := range str {
return string(unicode.ToLower(v)) + str[i+1:]
}
return ""
}
// executeTemplate executes the template for the specified file name and data
func executeTemplate(in *CmdIn, fileName string, data *GenerateData) (
filePath string, buf *bytes.Buffer, err error) {
filePath = filepath.Join(in.AppDir, in.Generate, fileName)
textTemplate, err := GetTemplate(fileName)
if err != nil {
return filePath, buf, err
}
t := template.Must(
template.New(fmt.Sprintf("generate%s", fileName)).Parse(textTemplate))
buf = new(bytes.Buffer)
err = t.Execute(buf, &data)
if err != nil {
return filePath, buf, errors.WithStack(err)
}
return filePath, buf, nil
}
// generateHelpers generates helper files, config.go, template.go, etc.
// These files can then be included by users in their own projects
// when they import the config package at the path as per the "generate" flag
func generateHelpers(in *CmdIn) (files []File, err error) {
// Generate data for executing template
data, err := NewGenerateData(in)
if err != nil {
return files, err
}
// NOTE buf is usually filled with content to be written to stdout.
// For the generate flag the contents of buf depends on the dry run flag,
// and that is checked elsewhere
// files contains file paths and generated code,
// depending on the dry run flag it may be written
// to stdout or the file system
files = make([]File, 3)
filePath, buf, err := executeTemplate(in, FileNameConfigGo, data)
if err != nil {
return files, err
}
files[0] = File{
Path: filePath,
Buf: bytes.NewBuffer(buf.Bytes()),
}
if len(data.TemplateKeys) > 0 {
filePath, buf, err = executeTemplate(in, FileNameTemplateGo, data)
if err != nil {
return files, err
}
files[1] = File{
Path: filePath,
Buf: bytes.NewBuffer(buf.Bytes()),
}
} else {
files[1] = File{
Path: "",
Buf: bytes.NewBuffer([]byte("")),
}
}
filePath, buf, err = executeTemplate(in, FileNameFnGo, data)
if err != nil {
return files, err
}
files[2] = File{
Path: filePath,
Buf: bytes.NewBuffer(buf.Bytes()),
}
return files, nil
}