package views import ( "bytes" "compress/gzip" "embed" "fmt" "html/template" "io" "net/http" "path/filepath" "strings" ) var ( TemplateCache map[string]*template.Template ) func Init(viewFS embed.FS, viewMap map[string][]string, funcMap map[string]any) error { var err error TemplateCache, err = initTemplates(viewFS, viewMap, funcMap) if err != nil { return fmt.Errorf("failed to initialize templates: %v", err) } return nil } func initTemplates(viewFS embed.FS, viewMap map[string][]string, funcMap map[string]any) (map[string]*template.Template, error) { cache := make(map[string]*template.Template) tmplFuncMap := template.FuncMap(funcMap) for name, paths := range viewMap { if len(paths) == 0 { continue } rootName := strings.TrimSuffix(filepath.Base(paths[0]), filepath.Ext(paths[0])) tmpl := template.New(rootName).Funcs(tmplFuncMap) var combined []byte for _, path := range paths { content, err := viewFS.ReadFile(path) if err != nil { return nil, fmt.Errorf("reading %s: %w", path, err) } combined = append(combined, content...) combined = append(combined, '\n') } parsed, err := tmpl.Parse(string(combined)) if err != nil { return nil, fmt.Errorf("parsing template %s: %w", name, err) } cache[name] = parsed } return cache, nil } func RenderHTML(w http.ResponseWriter, r *http.Request, name string, data any) error { tmpl, ok := TemplateCache[name] if !ok { http.Error(w, "template not found: "+name, http.StatusNotFound) return fmt.Errorf("template %q not found", name) } var buf bytes.Buffer if err := tmpl.ExecuteTemplate(&buf, tmpl.Name(), data); err != nil { return fmt.Errorf("failed to execute template %q: %w", name, err) } w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Header().Set("Vary", "Accept-Encoding") if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") { w.Header().Set("Content-Encoding", "gzip") gz := gzip.NewWriter(w) defer gz.Close() _, err := io.Copy(gz, &buf) return err } _, err := io.Copy(w, &buf) return err } func RenderHTMLToString(name string, data any) (string, error) { tmpl, ok := TemplateCache[name] if !ok { return "", fmt.Errorf("template %q not found", name) } var buf bytes.Buffer if err := tmpl.ExecuteTemplate(&buf, tmpl.Name(), data); err != nil { return "", fmt.Errorf("failed to execute template %q: %w", name, err) } return buf.String(), nil }