Files
uPortal/internal/template/service.go
T
2026-10-15 23:25:46 +03:00

91 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)
}