2019-09-04 15:22:27 +03:00
|
|
|
package core
|
|
|
|
|
|
|
|
import (
|
|
|
|
"html/template"
|
2021-07-29 11:29:31 +03:00
|
|
|
"io/fs"
|
2019-09-04 15:22:27 +03:00
|
|
|
|
|
|
|
"github.com/gin-contrib/multitemplate"
|
|
|
|
)
|
|
|
|
|
2021-02-09 14:57:14 +03:00
|
|
|
// Renderer wraps multitemplate.Renderer in order to make it easier to use.
|
2019-09-04 15:22:27 +03:00
|
|
|
type Renderer struct {
|
|
|
|
multitemplate.Renderer
|
2021-07-29 11:29:31 +03:00
|
|
|
TemplatesFS fs.FS
|
2019-11-28 11:32:49 +03:00
|
|
|
FuncMap template.FuncMap
|
|
|
|
alreadyAdded map[string]*template.Template
|
2019-09-04 15:22:27 +03:00
|
|
|
}
|
|
|
|
|
2021-02-09 14:57:14 +03:00
|
|
|
// NewRenderer is a Renderer constructor.
|
2019-09-04 15:22:27 +03:00
|
|
|
func NewRenderer(funcMap template.FuncMap) Renderer {
|
2019-11-28 12:13:58 +03:00
|
|
|
return newRendererWithMultitemplate(funcMap, multitemplate.NewRenderer())
|
|
|
|
}
|
|
|
|
|
2021-02-09 14:57:14 +03:00
|
|
|
// NewStaticRenderer is a Renderer constructor with multitemplate.Render.
|
2019-11-28 12:13:58 +03:00
|
|
|
func NewStaticRenderer(funcMap template.FuncMap) Renderer {
|
|
|
|
return newRendererWithMultitemplate(funcMap, multitemplate.New())
|
|
|
|
}
|
|
|
|
|
2021-02-09 14:57:14 +03:00
|
|
|
// NewDynamicRenderer is a Renderer constructor with multitemplate.DynamicRender.
|
2019-11-28 12:13:58 +03:00
|
|
|
func NewDynamicRenderer(funcMap template.FuncMap) Renderer {
|
|
|
|
return newRendererWithMultitemplate(funcMap, multitemplate.NewDynamic())
|
|
|
|
}
|
|
|
|
|
2021-02-09 14:57:14 +03:00
|
|
|
// newRendererWithMultitemplate initializes Renderer with provided multitemplate.Renderer instance.
|
2019-11-28 12:13:58 +03:00
|
|
|
func newRendererWithMultitemplate(funcMap template.FuncMap, renderer multitemplate.Renderer) Renderer {
|
2019-09-04 15:22:27 +03:00
|
|
|
return Renderer{
|
2019-11-28 12:13:58 +03:00
|
|
|
Renderer: renderer,
|
2019-11-28 11:32:49 +03:00
|
|
|
FuncMap: funcMap,
|
|
|
|
alreadyAdded: map[string]*template.Template{},
|
2019-09-04 15:22:27 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-09 14:57:14 +03:00
|
|
|
// Push is an AddFromFilesFuncs wrapper.
|
2019-09-04 15:22:27 +03:00
|
|
|
func (r *Renderer) Push(name string, files ...string) *template.Template {
|
2019-11-28 11:32:49 +03:00
|
|
|
if tpl := r.getTemplate(name); tpl != nil {
|
|
|
|
return tpl
|
|
|
|
}
|
|
|
|
|
2021-07-29 11:29:31 +03:00
|
|
|
if r.TemplatesFS != nil {
|
|
|
|
return r.storeTemplate(name, r.addFromFS(name, r.FuncMap, files...))
|
2019-09-18 10:28:55 +03:00
|
|
|
}
|
2019-12-12 09:35:05 +03:00
|
|
|
|
2021-07-29 11:29:31 +03:00
|
|
|
return r.storeTemplate(name, r.AddFromFilesFuncs(name, r.FuncMap, files...))
|
2019-09-04 15:22:27 +03:00
|
|
|
}
|
2019-09-18 10:28:55 +03:00
|
|
|
|
2021-07-29 11:29:31 +03:00
|
|
|
// addFromFS adds embedded template.
|
|
|
|
func (r *Renderer) addFromFS(name string, funcMap template.FuncMap, files ...string) *template.Template {
|
2021-12-01 15:40:23 +03:00
|
|
|
filesData := make([]string, len(files))
|
2019-09-18 10:28:55 +03:00
|
|
|
|
2021-12-01 15:40:23 +03:00
|
|
|
for i := 0; i < len(files); i++ {
|
|
|
|
data, err := fs.ReadFile(r.TemplatesFS, files[i])
|
2021-07-29 11:29:31 +03:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
2019-09-18 10:28:55 +03:00
|
|
|
}
|
2021-07-29 11:29:31 +03:00
|
|
|
|
2021-12-01 15:40:23 +03:00
|
|
|
filesData[i] = string(data)
|
2019-09-18 10:28:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return r.AddFromStringsFuncs(name, funcMap, filesData...)
|
2019-11-28 11:32:49 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// storeTemplate stores built template if multitemplate.DynamicRender is used.
|
|
|
|
// Dynamic render doesn't store templates - it stores builders, that's why we can't just extract them.
|
2019-12-12 09:35:05 +03:00
|
|
|
// It possibly can cause data inconsistency in developer environments where return value from Renderer.Push is used.
|
2019-11-28 11:32:49 +03:00
|
|
|
func (r *Renderer) storeTemplate(name string, tpl *template.Template) *template.Template {
|
|
|
|
if _, ok := r.Renderer.(multitemplate.DynamicRender); ok {
|
|
|
|
r.alreadyAdded[name] = tpl
|
|
|
|
}
|
|
|
|
|
|
|
|
return tpl
|
|
|
|
}
|
|
|
|
|
2021-02-09 14:57:14 +03:00
|
|
|
// getTemplate returns template from render or from storage.
|
2019-11-28 11:32:49 +03:00
|
|
|
func (r *Renderer) getTemplate(name string) *template.Template {
|
|
|
|
if renderer, ok := r.Renderer.(multitemplate.Render); ok {
|
|
|
|
if i, ok := renderer[name]; ok {
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := r.Renderer.(multitemplate.DynamicRender); ok {
|
|
|
|
if i, ok := r.alreadyAdded[name]; ok {
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|