forked from albrow/jobs
/
generate.go
168 lines (157 loc) · 5.11 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
// Copyright 2015 Alex Browne. All rights reserved.
// Use of this source code is governed by the MIT
// license, which can be found in the LICENSE file.
// File main.go is intended to be used with go generate.
// It reads the contents of any .lua file ins the scripts
// directory, then it generates a go source file called
// scritps.go which converts the file contents to a string
// and assigns each script to a variable so they can be invoked.
package main
import (
"bytes"
"github.com/albrow/jobs"
"go/build"
"io/ioutil"
"os"
"path/filepath"
"strings"
"text/template"
)
var (
// scriptsPath is the path of the directory which holds lua scripts.
scriptsPath string
// destPath is the path to a file where the generated go code will be written.
destPath string
// genTmplPath is the path to a .tmpl file which will be used to generate go code.
genTmplPath string
)
var (
// scriptContext is a map which is passed in as the context to all lua script templates.
// It holds the keys for all the different status sets, the names of the sets, and the keys
// for other constant sets.
scriptContext = map[string]string{
"statusSaved": string(jobs.StatusSaved),
"statusQueued": string(jobs.StatusQueued),
"statusExecuting": string(jobs.StatusExecuting),
"statusFinished": string(jobs.StatusFinished),
"statusFailed": string(jobs.StatusFailed),
"statusCancelled": string(jobs.StatusCancelled),
"statusDestroyed": string(jobs.StatusDestroyed),
"savedSet": jobs.StatusSaved.Key(),
"queuedSet": jobs.StatusQueued.Key(),
"executingSet": jobs.StatusExecuting.Key(),
"finishedSet": jobs.StatusFinished.Key(),
"failedSet": jobs.StatusFailed.Key(),
"cancelledSet": jobs.StatusCancelled.Key(),
"destroyedSet": jobs.StatusDestroyed.Key(),
"timeIndexSet": jobs.Keys.JobsTimeIndex,
"jobsTempSet": jobs.Keys.JobsTemp,
"activePoolsSet": jobs.Keys.ActivePools,
}
)
// script is a representation of a lua script file.
type script struct {
// VarName is the variable name that the script will be assigned to in the generated go code.
VarName string
// RawSrc is the contents of the original .lua file, which is a template.
RawSrc string
// Src is the the result of executing RawSrc as a template.
Src string
}
func init() {
// Use build to find the directory where this file lives. This always works as
// long as you have go installed, even if you have multiple GOPATHs or are using
// dependency management tools.
pkg, err := build.Import("github.com/albrow/jobs", "", build.FindOnly)
if err != nil {
panic(err)
}
// Configure the required paths
scriptsPath = filepath.Join(pkg.Dir, "scripts")
destPath = filepath.Clean(filepath.Join(scriptsPath, "..", "scripts.go"))
genTmplPath = filepath.Join(scriptsPath, "scripts.go.tmpl")
}
func main() {
scripts, err := findScripts(scriptsPath)
if err != nil {
panic(err)
}
if err := generateFile(scripts, genTmplPath, destPath); err != nil {
panic(err)
}
}
// findScripts finds all the .lua script files in the given path
// and creates a script object for each one. It returns a slice of
// scripts or an error if there was a problem reading any of the files.
func findScripts(path string) ([]*script, error) {
filenames, err := filepath.Glob(filepath.Join(path, "*.lua"))
if err != nil {
return nil, err
}
scripts := []*script{}
for _, filename := range filenames {
script := script{
VarName: convertUnderscoresToCamelCase(strings.TrimSuffix(filepath.Base(filename), ".lua")) + "Script",
}
src, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
script.RawSrc = string(src)
scripts = append(scripts, &script)
}
return scripts, nil
}
// convertUnderscoresToCamelCase converts a string of the form
// foo_bar_baz to fooBarBaz.
func convertUnderscoresToCamelCase(s string) string {
if len(s) == 0 {
return ""
}
result := ""
shouldUpper := false
for _, char := range s {
if char == '_' {
shouldUpper = true
continue
}
if shouldUpper {
result += strings.ToUpper(string(char))
} else {
result += string(char)
}
shouldUpper = false
}
return result
}
// generateFile generates go source code and writes to
// the source file located at dest (creating it if needed).
// It executes the template located at tmplFile with scripts
// as the context.
func generateFile(scripts []*script, tmplFile string, dest string) error {
// Treat the contents of the script file as a template and execute
// it with scriptsContext (a map of constant keys) as the data.
buf := bytes.NewBuffer([]byte{})
for _, script := range scripts {
scriptTmpl, err := template.New("script").Parse(script.RawSrc)
if err != nil {
return err
}
buf.Reset()
if err := scriptTmpl.Execute(buf, scriptContext); err != nil {
return err
}
script.Src = buf.String()
}
// Now generate the go source code by executing genTmpl. The generated
// code uses the Src property of the scripts as the argument to redis.NewScript.
genTmpl, err := template.ParseFiles(tmplFile)
if err != nil {
return err
}
destFile, err := os.Create(dest)
if err != nil {
return err
}
return genTmpl.Execute(destFile, scripts)
}