/
templates.go
177 lines (150 loc) · 4.24 KB
/
templates.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
package main
import (
"fmt"
"io"
"os"
"strconv"
"strings"
"text/template"
"github.com/nkovacs/streamquote"
"github.com/valyala/fasttemplate"
)
var (
tmplEmbeddedBox *template.Template
tagEscaper, tagUnescaper *strings.Replacer
)
const (
unescapeTag = "unescape:"
injectTag = "injectfile:"
)
func init() {
var err error
// $ is used as the escaping character,
// because it has no special meaning in go strings,
// so it won't be changed by strconv.Quote.
replacements := []string{"$", "$$", "{%", "{$%", "%}", "%$}"}
reverseReplacements := make([]string, len(replacements))
l := len(reverseReplacements) - 1
for i := range replacements {
reverseReplacements[l-i] = replacements[i]
}
tagEscaper = strings.NewReplacer(replacements...)
tagUnescaper = strings.NewReplacer(reverseReplacements...)
// parse embedded box template
tmplEmbeddedBox, err = template.New("embeddedBox").Funcs(template.FuncMap{
"tagescape": func(s string) string {
return fmt.Sprintf("{%%%v%v%%}", unescapeTag, tagEscaper.Replace(s))
},
"injectfile": func(s string) string {
return fmt.Sprintf("{%%%v%v%%}", injectTag, tagEscaper.Replace(s))
},
}).Parse(`package {{.Package}}
import (
"time"
"github.com/GeertJohan/go.rice/embedded"
)
{{range .Boxes}}
func init() {
// define files
{{range .Files}}{{.Identifier}} := &embedded.EmbeddedFile{
Filename: {{.FileName | tagescape | printf "%q"}},
FileModTime: time.Unix({{.ModTime}}, 0),
Content: string({{.Path | injectfile | printf "%q"}}),
}
{{end}}
// define dirs
{{range .Dirs}}{{.Identifier}} := &embedded.EmbeddedDir{
Filename: {{.FileName | tagescape | printf "%q"}},
DirModTime: time.Unix({{.ModTime}}, 0),
ChildFiles: []*embedded.EmbeddedFile{
{{range .ChildFiles}}{{.Identifier}}, // {{.FileName | tagescape | printf "%q"}}
{{end}}
},
}
{{end}}
// link ChildDirs
{{range .Dirs}}{{.Identifier}}.ChildDirs = []*embedded.EmbeddedDir{
{{range .ChildDirs}}{{.Identifier}}, // {{.FileName | tagescape | printf "%q"}}
{{end}}
}
{{end}}
// register embeddedBox
embedded.RegisterEmbeddedBox(` + "`" + `{{.BoxName}}` + "`" + `, &embedded.EmbeddedBox{
Name: ` + "`" + `{{.BoxName}}` + "`" + `,
Time: time.Unix({{.UnixNow}}, 0),
Dirs: map[string]*embedded.EmbeddedDir{
{{range .Dirs}}{{.FileName | tagescape | printf "%q"}}: {{.Identifier}},
{{end}}
},
Files: map[string]*embedded.EmbeddedFile{
{{range .Files}}{{.FileName | tagescape | printf "%q"}}: {{.Identifier}},
{{end}}
},
})
}
{{end}}`)
if err != nil {
fmt.Printf("error parsing embedded box template: %s\n", err)
os.Exit(-1)
}
}
// embeddedBoxFasttemplate will inject file contents and unescape {% and %}.
func embeddedBoxFasttemplate(w io.Writer, src string) error {
ft, err := fasttemplate.NewTemplate(src, "{%", "%}")
if err != nil {
return fmt.Errorf("error compiling fasttemplate: %s\n", err)
}
converter := streamquote.New()
_, err = ft.ExecuteFunc(w, func(w io.Writer, tag string) (int, error) {
if strings.HasPrefix(tag, unescapeTag) {
tag = strings.TrimPrefix(tag, unescapeTag)
return w.Write([]byte(tagUnescaper.Replace(tag)))
}
if !strings.HasPrefix(tag, injectTag) {
return 0, fmt.Errorf("invalid fasttemplate tag: %v", tag)
}
tag = strings.TrimPrefix(tag, injectTag)
fileName, err := strconv.Unquote("\"" + tag + "\"")
if err != nil {
return 0, fmt.Errorf("error unquoting filename %v: %v\n", tag, err)
}
f, err := os.Open(tagUnescaper.Replace(fileName))
if err != nil {
return 0, fmt.Errorf("error opening file %v: %v\n", fileName, err)
}
n, err := converter.Convert(f, w)
f.Close()
if err != nil {
return n, fmt.Errorf("error converting file %v: %v\n", fileName, err)
}
return n, nil
})
if err != nil {
return fmt.Errorf("error executing fasttemplate: %s\n", err)
}
return nil
}
type embedFileDataType struct {
Package string
Boxes []*boxDataType
}
type boxDataType struct {
BoxName string
UnixNow int64
Files []*fileDataType
Dirs map[string]*dirDataType
}
type fileDataType struct {
Identifier string
FileName string
Path string
ModTime int64
}
type dirDataType struct {
Identifier string
FileName string
Content []byte
ModTime int64
ChildDirs []*dirDataType
ChildFiles []*fileDataType
}