package httputils import ( "fmt" "io" "os" "path/filepath" "strings" "text/template" "time" ) type TemplateSet struct { templates *template.Template paths []string loadTimes map[string]time.Time } func newTemplateSet(partials *TemplateSet, paths ...string) TemplateSet { var partialPaths []string if partials == nil { partialPaths = make([]string, 0) } else { partialPaths = partials.paths } allPaths := append(partialPaths, paths...) modTimes := make(map[string]time.Time) for _, path := range allPaths { fileInfo, _ := os.Stat(path) modTimes[path] = fileInfo.ModTime() } templates := template.Must(template.ParseFiles(allPaths...)) return TemplateSet{ templates: templates, paths: allPaths, loadTimes: modTimes, } } func NewTemplateSet(paths ...string) TemplateSet { for i, path := range paths { paths[i] = fmt.Sprintf("%s/%s", templateFolder, path) } return newTemplateSet(&partials, paths...) } func (templateSet *TemplateSet) ExecuteTemplate(wr io.Writer, name string, data any) error { templateSet.reloadTemplatesIfModified() return templateSet.templates.ExecuteTemplate(wr, name, data) } func (templateSet *TemplateSet) reloadTemplatesIfModified() { for path, loadTime := range templateSet.loadTimes { fileInfo, _ := os.Stat(path) modTime := fileInfo.ModTime() if modTime.After(loadTime) { fmt.Printf("%s updated: reloading templates...\n", path) goto update } } return update: now := time.Now() for _, path := range templateSet.paths { templateSet.templates.ParseFiles(path) templateSet.loadTimes[path] = now } } func getTemplatePathsInDirectory(directory string) (paths []string, err error) { paths = make([]string, 0) err = filepath.Walk(directory, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if !info.IsDir() && strings.HasSuffix(path, ".html") { paths = append(paths, path) } return nil }) return } const templateFolder = "templates" const partialsFolder = templateFolder + "/partials" var paths, _ = getTemplatePathsInDirectory(partialsFolder) var partials = newTemplateSet(nil, paths...)