-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtemplate.go
176 lines (163 loc) · 5.45 KB
/
template.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
package templates
import (
"io"
"io/fs"
"path"
htmlTemplate "html/template"
textTemplate "text/template"
)
type TemplateName string
type TemplateFilename string
type TemplateFiles map[Format]TemplateFilename
type TemplateCommonFiles map[Format][]TemplateFilename
type FuncMap = textTemplate.FuncMap
type Templates struct {
cfg Configuration
}
type Template struct {
templates Templates
Name TemplateName
files TemplateFiles
common TemplateCommonFiles
funcs FuncMap
}
// Creates and configures a structure for working with templates
func NewTemplates(cfg Configuration) *Templates {
return &Templates{cfg: cfg}
}
// SetConfiguration installs or updates a configuration
func (ts *Templates) SetConfiguration(cfg Configuration) {
ts.cfg = cfg
}
// Get instantiates a template and looks for files that match the template.
func (ts *Templates) Get(path string) (*Template, error) {
name := TemplateName(path)
t := &Template{templates: *ts, Name: name}
if err := t.getFiles(); err != nil {
return nil, err
}
if err := t.getCommonTemplateFiles(); err != nil {
return nil, err
}
return t, nil
}
// Must is a helper that wraps a call to a function returning (*Template, error)
// and panics if the error is non-nil. It is intended for use in variable initializations
// such as
//
// var t = templates.Must(tmplts.Get("templates/email")).Funcs(funcMap)
func Must(t *Template, err error) *Template {
if err != nil {
panic(err)
}
return t
}
// getFiles scans the file system for files matching the template name and the formats
// described in the configuration. If the presence of a particular file format is marked
// as optional (`isRequired` == false) in the configuration, the file read error is suppressed
func (t *Template) getFiles() error {
fsys := *t.templates.cfg.TemplatesFS
t.files = make(TemplateFiles)
for format, opts := range t.templates.cfg.Formats {
filename := string(t.Name) + "." + opts.FileExtension
_, err := fsys.Open(filename)
if err != nil {
if !opts.IsRequired {
continue
}
return err
}
t.files[format] = TemplateFilename(filename)
}
return nil
}
// getCommonTemplateFiles scans the file system of common template files
// by path and for formats specified in the configuration
func (t *Template) getCommonTemplateFiles() error {
if t.templates.cfg.CommonTemplatesPath != nil {
t.common = make(TemplateCommonFiles)
for format, opts := range t.templates.cfg.Formats {
commonTemplates, err := fs.Glob(*t.templates.cfg.TemplatesFS, *t.templates.cfg.CommonTemplatesPath+"/*."+opts.FileExtension)
if err != nil {
return err
}
for _, ct := range commonTemplates {
t.common[format] = append(t.common[format], TemplateFilename(ct))
}
}
}
return nil
}
// Identical to the functions in packages `text/template` and `html/template`.
// Funcs adds the elements of the argument map to the template's function map.
// It must be called before the template is parsed.
// It panics if a value in the map is not a function with appropriate return
// type. However, it is legal to overwrite elements of the map. The return
// value is the template, so calls can be chained.
func (t *Template) Funcs(funcMap FuncMap) *Template {
t.funcs = funcMap
return t
}
// Identical to the functions in package `html/template`.
// Execute applies a parsed HTML template to the specified data object,
// writing the output to wr.
// If an error occurs executing the template or writing its output,
// execution stops, but partial results may already have been written to
// the output writer.
func (t *Template) ExecuteHtml(wr io.Writer, vars interface{}) error {
fsys := *t.templates.cfg.TemplatesFS
template := htmlTemplate.New(string(t.Name)).Funcs(t.funcs)
var err error
// add common templates
for _, filename := range t.common[Html] {
template, err = template.ParseFS(fsys, string(filename))
if err != nil {
return err
}
}
// add new main template
mainTemplateFilename := t.files[Html]
template, err = template.ParseFS(fsys, string(mainTemplateFilename))
if err != nil {
return err
}
mainTemplateName := path.Base(string(mainTemplateFilename))
return template.Funcs(t.funcs).ExecuteTemplate(wr, mainTemplateName, vars)
}
// Identical to the functions in package `text/template`.
// Execute applies a parsed text template to the specified data object,
// writing the output to wr.
// If an error occurs executing the template or writing its output,
// execution stops, but partial results may already have been written to
// the output writer.
func (t *Template) ExecuteText(wr io.Writer, vars interface{}) error {
fsys := *t.templates.cfg.TemplatesFS
template := textTemplate.New(string(t.Name)).Funcs(t.funcs)
var err error
// add common templates
for _, filename := range t.common[Text] {
template, err = template.ParseFS(fsys, string(filename))
if err != nil {
return err
}
}
// add new main template
mainTemplateFilename := t.files[Text]
template, err = template.ParseFS(fsys, string(mainTemplateFilename))
if err != nil {
return err
}
mainTemplateName := path.Base(string(mainTemplateFilename))
return template.Funcs(t.funcs).ExecuteTemplate(wr, mainTemplateName, vars)
}
// Execute applies a parsed HTML and text template to the specified data objects,
// writing the output to wr's.
func (t *Template) Execute(html io.Writer, text io.Writer, vars interface{}) error {
if err := t.ExecuteHtml(html, vars); err != nil {
return err
}
if err := t.ExecuteText(text, vars); err != nil {
return err
}
return nil
}