-
-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathtemplify.go
202 lines (180 loc) · 5.85 KB
/
templify.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
// templify is a tool to be used with 'go generate' to embed external template files into Go code.
//
//
// Scenario
//
// An often used scenario in developing go applications is to generate text (e.g. html pages)
// using the Go template packages. You have two choices: either editing the template in a
// dedicated file and to read this file at runtime from your application. Or you add a
// "big" string containing the template to the source of your app and parse this string to
// create the template.
//
// If you want to create a single file application, than the more handy solution with a
// dedicated file is not desirable. On the other hand to edit a complex html template within
// a Go string in the source is very uncomfortable.
//
// templify allows you to edit your template in an extra file and to generate an Go source
// file containing the embedded string.
//
//
// Usage
//
// Simply add a line
// //go:generate templify mytemplate.file
//
// for each template you want to embed. Every time you run a 'go generate' in the
// corresponding folder, the file 'mytemplate.go' will be created. It contains a
// function 'mytemplateTemplate' returning the template string.
//
// You may use 'templify mytemplate.file' directly on the command line.
//
//
// Switches
//
// Usage of templify: 'templify [switches] templatefilename'
// -e
// export the generated, the template returning function. Default (false) means
// the function will not be exported.
// -f
// no formatting of the generated source. Default false means source will be
// formatted with gofmt.
//
// -n string
// name of generated, the template returning function. Its name will have
// 'Template' attached. Will be set to $(basename -s .ext outputfile) if empty
// (default).
//
// -o string
// name of output file. Defaults to name of template file excluding
// extension + '.go'.
//
// -p string
// name of package to be used in generated code (default "main").
//
// -t string
// name of alternate code generation template file. If empty (default), then
// the embedded template will be used. Template variables supplied are:
// .Name, .Package, .Content
//
package main
//go:generate templify -p main -o embed.go embed.tpl
import (
"errors"
"flag"
"fmt"
"go/format"
"io/ioutil"
"os"
"path"
"strings"
"text/template"
)
var pckg string
var inputfile string
var outfilename string
var functionname string
var tmplname string
var frmt bool
var exp bool
func flagging() {
flag.StringVar(&pckg, "p", "main", "name of package to be used in generated code.")
flag.StringVar(&outfilename, "o", "", "name of output file. Defaults to name of template file + '.go'.")
flag.StringVar(&functionname, "f", "", "name of generated, the template returning function. Its name will "+
"have 'Template' attached. Will be set to $(basename -s .ext outputfile) if empty (default).")
flag.StringVar(&tmplname, "t", "", "name of alternate code generation template file. If empty (default), "+
"then the embedded template will be used. Template variables supplied are: .Name, .Package, .Content")
flag.BoolVar(&frmt, "n", false, "do not format the generated source. Default false means source will be formatted.")
flag.BoolVar(&exp, "e", false, "export the generated, the template returning function. "+
"Default (false) means the function will not be exported.")
flag.Parse()
inputfile = flag.Arg(0)
if inputfile == "" {
fmt.Println(errors.New("no template file given as argument"))
os.Exit(1)
}
if outfilename == "" {
indir := path.Dir(inputfile)
inext := path.Ext(path.Base(inputfile))
inname := strings.TrimSuffix(path.Base(inputfile), inext)
outfilename = fmt.Sprintf("%s/%s.go", indir, inname)
}
if functionname == "" {
ext := path.Ext(path.Base(outfilename))
functionname = strings.TrimSuffix(path.Base(outfilename), ext)
}
if exp {
functionname = strings.ToUpper(functionname[0:1]) + functionname[1:]
}
}
func readTemplifyTemplate(tplname string) (*template.Template, error) {
tpl, err := template.ParseFiles(tplname)
if err != nil {
fmt.Printf("Error reading templifytemplate file '%s'\n%v", tplname, err)
}
return tpl, err
}
func readTargetTemplate(tplname string) string {
tpl, err := ioutil.ReadFile(tplname)
if err != nil {
fmt.Printf("Error reading target template file '%s'\n%v", tplname, err)
os.Exit(1)
}
r := strings.NewReplacer("\"", "\\\"", "\n", "\\n\" +\n\t\"", "\t", "\\t", "\r", "\\r", "\\", "\\\\")
return r.Replace(string(tpl))
}
func formatFile(fname string) {
fstr, err := ioutil.ReadFile(fname)
if err != nil {
fmt.Printf("Error reading generated file %s before passing it to gofmt.\n%v\n", fname, err)
os.Exit(1)
} else {
fstr, err = format.Source(fstr)
if err != nil {
fmt.Printf("Error running gofmt on the generated file '%s'\n%v\n", fname, err)
os.Exit(1)
} else {
foutfile, err := os.Create(fname)
if err != nil {
fmt.Printf("Error creating formatted target file '%s'\n%v\n", fname, err)
os.Exit(1)
} else {
defer foutfile.Close()
fmt.Fprintf(foutfile, "%s", fstr)
}
}
}
}
func main() {
flagging()
var tpl *template.Template
var err error
if tmplname != "" {
tpl, err = readTemplifyTemplate(tmplname)
} else {
tpl, err = template.New("embed").Parse(embedTemplate())
}
//tpl, err := template.New("embed").Parse(internalTemplate)
if err != nil {
fmt.Printf("Error parsing code generation template\n%v", err)
os.Exit(1)
}
data := struct {
Package string
Name string
Content string
}{
Package: pckg,
Name: strings.Split(functionname, ".")[0],
}
data.Content = readTargetTemplate(inputfile)
outfile, err := os.Create(outfilename)
if err != nil {
fmt.Printf("Error creating target file '%s'\n%v\n", outfilename, err)
os.Exit(1)
}
defer outfile.Close()
tpl.Execute(outfile, data)
if !frmt {
formatFile(outfilename)
}
}