ytdl-web/pkg/view/html/engine.go

126 lines
2.3 KiB
Go

package html
import (
"embed"
"html/template"
"io/fs"
"net/http"
"path/filepath"
"strings"
"sync"
"go.fifitido.net/ytdl-web/pkg/view"
)
type Engine struct {
fs embed.FS
tpl *template.Template
opts *Options
loadOnce sync.Once
mu sync.Mutex
}
var _ view.Engine = (*Engine)(nil)
func New(fs embed.FS, options ...*Options) *Engine {
var opts *Options
if len(options) > 0 && options[0] != nil {
opts = options[0]
} else {
opts = DefaultOptions()
}
tpl := template.New("/")
tpl.Delims(opts.LeftDelim, opts.RightDelim)
tpl.Funcs(opts.FuncMap)
return &Engine{
fs: fs,
tpl: tpl,
opts: opts,
}
}
func (e *Engine) Load() error {
var err error
e.loadOnce.Do(func() {
err = e.doLoad()
})
return err
}
func (e *Engine) doLoad() error {
e.mu.Lock()
defer e.mu.Unlock()
return fs.WalkDir(e.fs, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d == nil || d.IsDir() {
return nil
}
if len(path) < len(e.opts.Extension) || path[len(path)-len(e.opts.Extension):] != e.opts.Extension {
return nil
}
rel, err := filepath.Rel(e.opts.BaseDir, path)
if err != nil {
return err
}
name := filepath.ToSlash(rel)
name = strings.TrimSuffix(name, e.opts.Extension)
buf, err := e.fs.ReadFile(path)
if err != nil {
return err
}
_, err = e.tpl.New(name).Parse(string(buf))
if err != nil {
return err
}
return nil
})
}
func (e *Engine) Render(w http.ResponseWriter, view string, data view.Data, layout ...string) {
tmpl := e.tpl.Lookup(view)
if tmpl == nil {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
if len(layout) > 0 && layout[0] != "" {
lay := e.tpl.Lookup(layout[0])
if lay == nil {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
e.mu.Lock()
defer e.mu.Unlock()
lay.Funcs(map[string]interface{}{
"yield": func() error {
return tmpl.Execute(w, data)
},
})
if err := lay.Execute(w, data); err != nil {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
return
}
if err := tmpl.Execute(w, data); err != nil {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
}