go/webutil/template.go
2024-11-11 06:36:55 +01:00

101 lines
2.3 KiB
Go

package webutil
import (
"embed"
"html/template"
"io/fs"
"log"
"path"
"strings"
)
// ParseTemplateSet parses sets of templates from an embed.FS.
//
// Each directory constitutes a set of templates that are parsed together.
//
// Structure (within a directory):
// - share/* are always parsed.
// - base.html will be parsed with each other file in same dir
//
// Call a template with m[path].Execute(w, data) (root dir name is excluded).
//
// For example, if you have
// - /user/share/*
// - /user/base.html
// - /user/home.html
//
// Then you call m["/user/home.html"].Execute(w, data).
func ParseTemplateSet(funcs template.FuncMap, fs embed.FS) map[string]*template.Template {
m := map[string]*template.Template{}
rootDir := readDir(fs, ".")[0].Name()
loadTemplateDir(fs, funcs, m, rootDir, rootDir)
return m
}
func loadTemplateDir(
fs embed.FS,
funcs template.FuncMap,
m map[string]*template.Template,
dirPath string,
rootDir string,
) map[string]*template.Template {
t := template.New("")
if funcs != nil {
t = t.Funcs(funcs)
}
shareDir := path.Join(dirPath, "share")
if _, err := fs.ReadDir(shareDir); err == nil {
log.Printf("Parsing %s...", path.Join(shareDir, "*"))
t = template.Must(t.ParseFS(fs, path.Join(shareDir, "*")))
}
if data, _ := fs.ReadFile(path.Join(dirPath, "base.html")); data != nil {
log.Printf("Parsing %s...", path.Join(dirPath, "base.html"))
t = template.Must(t.Parse(string(data)))
}
for _, ent := range readDir(fs, dirPath) {
if ent.Type().IsDir() {
if ent.Name() != "share" {
m = loadTemplateDir(fs, funcs, m, path.Join(dirPath, ent.Name()), rootDir)
}
continue
}
if !ent.Type().IsRegular() {
continue
}
if ent.Name() == "base.html" {
continue
}
filePath := path.Join(dirPath, ent.Name())
log.Printf("Parsing %s...", filePath)
key := strings.TrimPrefix(path.Join(dirPath, ent.Name()), rootDir)
tt := template.Must(t.Clone())
tt = template.Must(tt.Parse(readFile(fs, filePath)))
m[key] = tt
}
return m
}
func readDir(fs embed.FS, dirPath string) []fs.DirEntry {
ents, err := fs.ReadDir(dirPath)
if err != nil {
panic(err)
}
return ents
}
func readFile(fs embed.FS, path string) string {
data, err := fs.ReadFile(path)
if err != nil {
panic(err)
}
return string(data)
}