package httpserver import ( "bytes" "fmt" "html/template" "net/http" "path/filepath" "strings" ) type Renderer struct { templates map[string]*template.Template } func NewRenderer(templatesDir string) (*Renderer, error) { layouts, err := filepath.Glob(filepath.Join(templatesDir, "layouts", "*.gohtml")) if err != nil { return nil, fmt.Errorf("find layouts: %w", err) } if len(layouts) == 0 { return nil, fmt.Errorf("no layout templates found in %s", filepath.Join(templatesDir, "layouts")) } partials, err := filepath.Glob(filepath.Join(templatesDir, "partials", "*.gohtml")) if err != nil { return nil, fmt.Errorf("find partials: %w", err) } pages, err := filepath.Glob(filepath.Join(templatesDir, "pages", "*.gohtml")) if err != nil { return nil, fmt.Errorf("find pages: %w", err) } if len(pages) == 0 { return nil, fmt.Errorf("no page templates found in %s", filepath.Join(templatesDir, "pages")) } renderer := &Renderer{templates: make(map[string]*template.Template, len(pages))} for _, page := range pages { files := append([]string{}, layouts...) files = append(files, partials...) files = append(files, page) tmpl, err := template.ParseFiles(files...) if err != nil { return nil, fmt.Errorf("parse template set for %s: %w", page, err) } name := strings.TrimSuffix(filepath.Base(page), filepath.Ext(page)) renderer.templates[name] = tmpl } return renderer, nil } func (r *Renderer) Render(w http.ResponseWriter, name string, status int, data any) error { tmpl, ok := r.templates[name] if !ok { return fmt.Errorf("unknown template %q", name) } var output bytes.Buffer if err := tmpl.ExecuteTemplate(&output, "base", data); err != nil { return err } w.Header().Set("Content-Type", "text/html; charset=utf-8") if status <= 0 { status = http.StatusOK } w.WriteHeader(status) _, err := output.WriteTo(w) return err }