/
tmpl.go
138 lines (124 loc) · 3.66 KB
/
tmpl.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
package fsutil
import (
"bytes"
htmltmpl "html/template"
"net/http"
"path/filepath"
"strings"
txttmpl "text/template"
"github.com/pkg/errors"
)
// TmplParse parses templates from the given filesystem according to the
// given paths. If tmpl is not nil, the templates will be added to it.
// paths must contain at least one path. All paths must exist in the
// given filesystem.
func TmplParse(fs http.FileSystem, tmpl *txttmpl.Template, paths ...string) (*txttmpl.Template, error) {
t := tmplParser{Template: tmpl}
err := parseFiles(fs, t.parse, paths...)
return t.Template, err
}
// TmplParseGlob parses templates from the given filesystem according to
// the provided glob pattern. If tmpl is not nil, the templates will be
// added to it.
func TmplParseGlob(fs http.FileSystem, tmpl *txttmpl.Template, pattern string) (*txttmpl.Template, error) {
t := tmplParser{Template: tmpl}
err := parseGlob(fs, t.parse, pattern)
return t.Template, err
}
// TmplParseHTML parses HTML templates from the given filesystem according
// to the given paths. If tmpl is not nil, the templates will be added to
// it. paths must contain at least one path. All paths must exist in the
// given filesystem.
func TmplParseHTML(fs http.FileSystem, tmpl *htmltmpl.Template, paths ...string) (*htmltmpl.Template, error) {
t := tmplParserHTML{Template: tmpl}
err := parseFiles(fs, t.parse, paths...)
return t.Template, err
}
// TmplParseGlobHTML parses HTML templates from the given filesystem
// according to the provided glob pattern. If tmpl is not nil, the
// templates will be added to it.
func TmplParseGlobHTML(fs http.FileSystem, tmpl *htmltmpl.Template, pattern string) (*htmltmpl.Template, error) {
t := tmplParserHTML{Template: tmpl}
err := parseGlob(fs, t.parse, pattern)
return t.Template, err
}
type tmplParser struct {
*txttmpl.Template
}
func (t *tmplParser) parse(name, content string) error {
var err error
if t.Template == nil {
t.Template = txttmpl.New(name)
} else {
t.Template = t.New(name)
}
t.Template, err = t.Parse(content)
return err
}
type tmplParserHTML struct {
*htmltmpl.Template
}
func (t *tmplParserHTML) parse(name, content string) error {
var err error
if t.Template == nil {
t.Template = htmltmpl.New(name)
} else {
t.Template = t.New(name)
}
t.Template, err = t.Parse(content)
return err
}
func parseFiles(fs http.FileSystem, parse func(name string, content string) error, paths ...string) error {
if len(paths) == 0 {
return errors.New("no paths provided")
}
buf := bytes.NewBuffer(nil)
for _, path := range paths {
f, err := fs.Open(strings.Trim(path, "/"))
if err != nil {
return errors.Wrapf(err, "opening template %s", path)
}
name := filepath.Base(path)
buf.Reset()
buf.ReadFrom(f)
err = parse(name, buf.String())
if err != nil {
return errors.Wrapf(err, "parsing template %s", path)
}
}
return nil
}
func parseGlob(fs http.FileSystem, parse func(name string, content string) error, pattern string) error {
buf := bytes.NewBuffer(nil)
walker := Walk(fs, "")
for walker.Step() {
matched, err := filepath.Match(pattern, walker.Path())
if err != nil {
return err
}
if !matched {
continue
}
f, err := fs.Open(walker.Path())
if err != nil {
return errors.Wrapf(err, "opening template %s", walker.Path())
}
st, err := f.Stat()
if err != nil {
return errors.Wrapf(err, "stat %s", walker.Path())
}
if st.IsDir() {
continue
}
buf.Reset()
buf.ReadFrom(f)
err = parse(walker.Stat().Name(), buf.String())
if err != nil {
return errors.Wrapf(err, "parsing template %s", walker.Path())
}
}
if err := walker.Err(); err != nil {
return errors.Wrap(err, "failed walking filesystem")
}
return nil
}