You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

146 lines
2.8 KiB
Go

package main
import (
"html/template"
"io"
"io/fs"
"path/filepath"
"strings"
txttmpl "text/template"
)
// Options for getting a template tree
type Options struct {
Globs []string
Prefix string
Suffix string
Root string
Funcs map[string]interface{}
}
func (o Options) getRoot() string {
if o.Root == "" {
return "."
}
return o.Root
}
func (o Options) matchesGlob(path string) (bool, error) {
if len(o.Globs) == 0 {
return true, nil
}
for _, glob := range o.Globs {
ok, err := filepath.Match(glob, path)
if err != nil {
return false, err
}
if ok {
return true, nil
}
}
return false, nil
}
func (o Options) trim(path string) string {
return strings.TrimSuffix(strings.TrimPrefix(path, o.Prefix), o.Suffix)
}
// Templates returns templates based on a filesystem
func Templates(files fs.FS, opts Options) (*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 opts.Funcs != nil {
tmpl = tmpl.Funcs(template.FuncMap(opts.Funcs))
}
} else {
tmpl = ret.New(name)
}
tmpl, err := tmpl.Parse(string(body))
if err != nil {
return err
}
ret = tmpl
return nil
}
if err := walk(files, opts, parseTemplates); err != nil {
return nil, err
}
return ret, nil
}
// TextTemplates returns templates based on a filesystem
func TextTemplates(files fs.FS, opts Options) (*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 opts.Funcs != nil {
tmpl = tmpl.Funcs(txttmpl.FuncMap(opts.Funcs))
}
} else {
tmpl = ret.New(name)
}
tmpl, err := tmpl.Parse(string(body))
if err != nil {
return err
}
ret = tmpl
return nil
}
if err := walk(files, opts, parseTemplates); err != nil {
return nil, err
}
return ret, nil
}
func walk(files fs.FS, opts Options, handle func(string, []byte) error) error {
walker := func(path string, d fs.DirEntry, er error) (err error) {
if er != nil {
return er
}
matches, err := opts.matchesGlob(path)
if err != nil {
return err
}
if !matches {
return nil
}
body, err := readfile(files, path)
if err != nil {
return err
}
if err := handle(opts.trim(path), body); err != nil {
return err
}
return nil
}
if err := fs.WalkDir(files, opts.getRoot(), walker); err != nil {
return err
}
return nil
}
func readfile(files fs.FS, path string) ([]byte, error) {
f, err := files.Open(path)
if err != nil {
return nil, err
}
body, err := io.ReadAll(f)
if err != nil {
if err := f.Close(); err != nil {
return nil, err
}
return nil, err
}
if err := f.Close(); err != nil {
return nil, err
}
return body, nil
}