-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathengine.go
182 lines (161 loc) · 4.37 KB
/
engine.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
package template
import (
"embed"
"fmt"
"io"
"os"
"path/filepath"
"github.com/fsnotify/fsnotify"
)
// LoadTemplateFunc 加载模板函数类
type LoadTemplateFunc func(templatesDir string, funcMap FuncMap) Render
// LoadEmbedFSTemplateFunc 加载模板函数类
type LoadEmbedFSTemplateFunc func(tmplFS *embed.FS, tmplFSSUbDir string, funcMap FuncMap) Render
// Engine 模板引擎
type Engine struct {
templatesDir string
tmplFS *embed.FS
tmplFSSUbDir string
watcher *fsnotify.Watcher
Errors <-chan error
loadTemplateFunc LoadTemplateFunc
loadTemplateEmbedFS LoadEmbedFSTemplateFunc
FuncMap FuncMap
HTMLRender Render
opts Options
}
// NewEngine 创建一个gin引擎模板
func NewEngine(templateDir string, tmplFunc LoadTemplateFunc, funcMap FuncMap, opts ...Option) (*Engine, error) {
watcher, err := fsnotify.NewWatcher()
if err != nil {
return nil, err
}
return &Engine{
templatesDir: templateDir,
loadTemplateFunc: tmplFunc,
watcher: watcher,
Errors: make(<-chan error),
FuncMap: funcMap,
opts: newOptions(opts...),
}, nil
}
func NewEngineWithEmbedFS(tmplFS *embed.FS, tmplFSSUbDir string, tmplFunc LoadEmbedFSTemplateFunc, funcMap FuncMap, opts ...Option) (*Engine, error) {
return &Engine{
tmplFS: tmplFS,
tmplFSSUbDir: tmplFSSUbDir,
loadTemplateEmbedFS: tmplFunc,
Errors: make(<-chan error),
FuncMap: funcMap,
opts: newOptions(opts...),
}, nil
}
// Init 初始化
func (en *Engine) Init() {
en.HTMLRender = en.loadTemplate()
}
// Watching 监听模板文件夹中是否有变动
func (en *Engine) Watching() error {
if en.templatesDir == "" {
return fmt.Errorf("template directory is empty")
}
if en.watcher == nil {
return fmt.Errorf("watcher is nil")
}
en.Errors = en.watcher.Errors
go func() {
for {
event := <-en.watcher.Events
loadFlag := true
switch event.Op {
case fsnotify.Create:
fileInfo, err := os.Stat(event.Name)
if err == nil && fileInfo.IsDir() {
en.watcher.Add(event.Name)
}
case fsnotify.Remove, fsnotify.Rename:
fileInfo, err := os.Stat(event.Name)
if err == nil && fileInfo.IsDir() {
en.watcher.Remove(event.Name)
}
case fsnotify.Chmod:
loadFlag = false
}
if loadFlag {
en.HTMLRender = en.loadTemplate()
}
}
}()
//遍历目录下的所有子目录
err := filepath.Walk(en.templatesDir, func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
err := en.watcher.Add(path)
if err != nil {
return err
}
}
return nil
})
if err != nil {
return err
}
return nil
}
// Close 关闭
func (en *Engine) Close() error {
if en.watcher == nil {
return nil
}
return en.watcher.Close()
}
// loadTemplate 加载模板
func (en *Engine) loadTemplate() Render {
if en.templatesDir != "" {
return en.loadTemplateFunc(en.templatesDir, en.FuncMap)
} else {
return en.loadTemplateEmbedFS(en.tmplFS, en.tmplFSSUbDir, en.FuncMap)
}
}
// PageName 页面
func (en *Engine) PageName(name string) string {
return fmt.Sprintf("%s:pages/%s", en.opts.Layout, name)
}
// SingleName 单页
func (en *Engine) SingleName(name string) string {
return fmt.Sprintf("singles/%s.tmpl", name)
}
// ErrorName 单页
func (en *Engine) ErrorName(name string) string {
return fmt.Sprintf("error/%s.tmpl", name)
}
// RenderPage 渲染页面
func (en *Engine) RenderPage(w io.Writer, name string, data H, opts ...Option) {
en.render(w, name, "page", data, opts...)
}
// RenderSingle 渲染单页面
func (en *Engine) RenderSingle(w io.Writer, name string, data H, opts ...Option) {
en.render(w, name, "single", data, opts...)
}
// RenderError 渲染错误页面
func (en *Engine) RenderError(w io.Writer, name string, data H, opts ...Option) {
en.render(w, name, "error", data, opts...)
}
// render 渲染
func (en *Engine) render(w io.Writer, name, typ string, data H, opts ...Option) {
opt := en.opts
for _, o := range opts {
o(&opt)
}
if data == nil {
data = H{}
}
data["constant"] = en.opts.GlobalConstant
data["variable"] = en.opts.GlobalVariable
switch typ {
case "page":
en.HTMLRender.Execute(en.PageName(name), w, data)
case "single":
en.HTMLRender.Execute(en.SingleName(name), w, data)
case "error":
en.HTMLRender.Execute(en.ErrorName(name), w, data)
}
}