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, ".") }