90 lines
1.8 KiB
Go
90 lines
1.8 KiB
Go
package template
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"html/template"
|
||
|
|
"io"
|
||
|
|
"io/fs"
|
||
|
|
"os"
|
||
|
|
"path"
|
||
|
|
"strings"
|
||
|
|
)
|
||
|
|
|
||
|
|
type Service struct {
|
||
|
|
baseDir string
|
||
|
|
cache map[string]*template.Template
|
||
|
|
}
|
||
|
|
|
||
|
|
func createFuncMap() template.FuncMap {
|
||
|
|
funcMap := template.FuncMap{
|
||
|
|
"dict": func(pairs ...any) (map[string]any, error) {
|
||
|
|
if len(pairs)%2 != 0 {
|
||
|
|
return nil, fmt.Errorf("dict requires an even number of arguments")
|
||
|
|
}
|
||
|
|
m := make(map[string]any)
|
||
|
|
for i := 0; i < len(pairs); i += 2 {
|
||
|
|
key, ok := pairs[i].(string)
|
||
|
|
if !ok {
|
||
|
|
return nil, fmt.Errorf("dict keys must be strings")
|
||
|
|
}
|
||
|
|
m[key] = pairs[i+1]
|
||
|
|
}
|
||
|
|
return m, nil
|
||
|
|
},
|
||
|
|
"default": func(fallback string, value any) any {
|
||
|
|
if value == nil {
|
||
|
|
return fallback
|
||
|
|
}
|
||
|
|
return value
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
return funcMap
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewService(uiDir string) (*Service, error) {
|
||
|
|
cache := make(map[string]*template.Template)
|
||
|
|
|
||
|
|
uiFS := os.DirFS(uiDir)
|
||
|
|
|
||
|
|
funcMap := createFuncMap()
|
||
|
|
|
||
|
|
err := fs.WalkDir(uiFS, "pages", func(filePath string, d fs.DirEntry, err error) error {
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if d.IsDir() {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
if !strings.HasSuffix(filePath, ".html") {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Key by the relative path under pages/, e.g. "auth/signin.html".
|
||
|
|
relPath := strings.TrimPrefix(filePath, "pages/")
|
||
|
|
|
||
|
|
tmpl, err := template.New(path.Base(filePath)).Funcs(funcMap).ParseFS(uiFS, filePath, "layouts/*.html")
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("can't parse filesystem: %w", err)
|
||
|
|
}
|
||
|
|
cache[relPath] = tmpl
|
||
|
|
|
||
|
|
return nil
|
||
|
|
})
|
||
|
|
if err != nil {
|
||
|
|
return nil, fmt.Errorf("can't walk 'pages' in '%s': %w", uiDir, err)
|
||
|
|
}
|
||
|
|
|
||
|
|
return &Service{
|
||
|
|
baseDir: uiDir,
|
||
|
|
cache: cache,
|
||
|
|
}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (e *Service) Render(w io.Writer, pageName string, data any) error {
|
||
|
|
tmpl, ok := e.cache[pageName]
|
||
|
|
if !ok {
|
||
|
|
return fmt.Errorf("no such template: %s", pageName)
|
||
|
|
}
|
||
|
|
return tmpl.Execute(w, data)
|
||
|
|
}
|