|
|
|
package tmpl
|
|
|
|
|
|
|
|
import (
|
|
|
|
"html/template"
|
|
|
|
"io/fs"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
txttmpl "text/template"
|
|
|
|
"unicode/utf8"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Templates for getting a template tree
|
|
|
|
type Templates struct {
|
|
|
|
Suffix string
|
|
|
|
Root string
|
|
|
|
Funcs map[string]interface{}
|
|
|
|
fs.FS
|
|
|
|
}
|
|
|
|
|
|
|
|
// HTML returns templates based on a filesystem
|
|
|
|
func (t Templates) HTML(name string) (*template.Template, error) {
|
|
|
|
var ret *template.Template
|
|
|
|
parseTemplates := func(name string, body []byte) error {
|
|
|
|
var tmpl *template.Template
|
|
|
|
if ret == nil {
|
|
|
|
tmpl = template.New(name)
|
|
|
|
if t.Funcs != nil {
|
|
|
|
tmpl = tmpl.Funcs(template.FuncMap(t.Funcs))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
tmpl = ret.New(name)
|
|
|
|
}
|
|
|
|
tmpl, err := tmpl.Parse(string(body))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
ret = tmpl
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if err := t.walk(parseTemplates); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if t := ret.Lookup(name); t != nil {
|
|
|
|
return t, nil
|
|
|
|
}
|
|
|
|
return ret, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Text returns templates based on a filesystem
|
|
|
|
func (t Templates) Text(name string) (*txttmpl.Template, error) {
|
|
|
|
var ret *txttmpl.Template
|
|
|
|
parseTemplates := func(name string, body []byte) error {
|
|
|
|
var tmpl *txttmpl.Template
|
|
|
|
if ret == nil {
|
|
|
|
tmpl = txttmpl.New(name)
|
|
|
|
if t.Funcs != nil {
|
|
|
|
tmpl = tmpl.Funcs(txttmpl.FuncMap(t.Funcs))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
tmpl = ret.New(name)
|
|
|
|
}
|
|
|
|
tmpl, err := tmpl.Parse(string(body))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
ret = tmpl
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := t.walk(parseTemplates); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if t := ret.Lookup(name); t != nil {
|
|
|
|
return t, nil
|
|
|
|
}
|
|
|
|
return ret, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// List returns a list of files
|
|
|
|
func (t Templates) List() ([]string, error) {
|
|
|
|
ret := []string{}
|
|
|
|
err := t.walk(func(name string, _ []byte) error {
|
|
|
|
ret = append(ret, name)
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return ret, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t Templates) walk(handle func(string, []byte) error) error {
|
|
|
|
return fs.WalkDir(t.FS, t.getRoot(), func(path string, d fs.DirEntry, er error) (err error) {
|
|
|
|
if er != nil {
|
|
|
|
return er
|
|
|
|
}
|
|
|
|
if d.IsDir() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if !strings.HasSuffix(path, t.Suffix) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
body, err := fs.ReadFile(t, path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !utf8.Valid(body) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return handle(t.trim(path), body)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t Templates) getRoot() string {
|
|
|
|
if t.Root == "" {
|
|
|
|
return "."
|
|
|
|
}
|
|
|
|
return filepath.Clean(t.Root)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t Templates) trim(path string) string {
|
|
|
|
cleaned := strings.TrimPrefix(filepath.Clean(path), filepath.Clean(t.Root))
|
|
|
|
cleaned = strings.TrimPrefix(cleaned, "/")
|
|
|
|
cleaned = strings.TrimSuffix(cleaned, t.Suffix)
|
|
|
|
return strings.TrimSuffix(cleaned, ".")
|
|
|
|
}
|