package echo import ( "html/template" "io" "io/fs" "net/http" "path/filepath" "github.com/labstack/echo/v4" ) type TemplateChecker interface { HaveTemplate(string) bool } type TemplateRenderer struct { templates fs.FS glob string funcs template.FuncMap cache *template.Template debug bool } func NewTemplateRenderer(templates fs.FS, glob string, funcs template.FuncMap, debug bool) (*TemplateRenderer, error) { var err error r := &TemplateRenderer{ templates: templates, glob: glob, funcs: funcs, debug: debug, } r.cache, err = r.loadTemplates() if err != nil { return nil, err } return r, nil } func (r *TemplateRenderer) loadTemplates() (*template.Template, error) { t := template.New("").Funcs(r.funcs) err := fs.WalkDir(r.templates, ".", func(path string, info fs.DirEntry, err error) error { if err != nil { return err } if !info.IsDir() { _, fn := filepath.Split(path) if ok, _ := filepath.Match(r.glob, fn); ok { fc, err := fs.ReadFile(r.templates, path) if err != nil { return err } _, err = t.New(path).Parse(string(fc)) if err != nil { return err } } } return nil }) if err != nil { return nil, err } return t, nil } func (r *TemplateRenderer) getTemplateCache() (*template.Template, error) { if !r.debug { return r.cache, nil } tc, err := r.loadTemplates() if err != nil { return nil, err } return tc, nil } func (r *TemplateRenderer) Render(w io.Writer, name string, data interface{}, ctx echo.Context) error { tc, err := r.getTemplateCache() if err != nil { return err } if tc.Lookup(name) == nil { return echo.NewHTTPError(http.StatusNotFound, "Page not found") } if err := tc.ExecuteTemplate(w, name, data); err != nil { return err } return nil } func (r *TemplateRenderer) HaveTemplate(name string) bool { tc, err := r.getTemplateCache() if err != nil { return false } return tc.Lookup(name) != nil } var _ echo.Renderer = (*TemplateRenderer)(nil) var _ TemplateChecker = (*TemplateRenderer)(nil)