depot/web/fup/fuphttp/fuphttp.go

83 lines
1.8 KiB
Go

// SPDX-FileCopyrightText: 2021 Luke Granger-Brown <depot@lukegb.com>
//
// SPDX-License-Identifier: Apache-2.0
package fuphttp
import (
"context"
"fmt"
"io/fs"
"net/http"
"github.com/google/safehtml/template"
"github.com/google/safehtml/template/uncheckedconversions"
"github.com/gorilla/mux"
)
type Config struct {
Templates fs.FS
Static fs.FS
StaticRoot string
}
type Application struct {
indexTmpl *template.Template
notFoundTmpl *template.Template
}
func (a *Application) Handler() http.Handler {
r := mux.NewRouter()
r.NotFoundHandler = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
rw.WriteHeader(http.StatusNotFound)
a.notFoundTmpl.Execute(rw, nil)
})
return r
}
func parseTemplate(t *template.Template, fsys fs.FS, name string) (*template.Template, error) {
bs, err := fs.ReadFile(fsys, name)
if err != nil {
return nil, fmt.Errorf("reading template %q: %w", name, err)
}
return t.ParseFromTrustedTemplate(
uncheckedconversions.TrustedTemplateFromStringKnownToSatisfyTypeContract(string(bs)),
)
}
func loadTemplate(fsys fs.FS, name string) (*template.Template, error) {
t := template.New(name)
var err error
if t, err = parseTemplate(t, fsys, "base.html"); err != nil {
return nil, fmt.Errorf("loading base template: %w", err)
}
if t, err = parseTemplate(t, fsys, fmt.Sprintf("%s.html", name)); err != nil {
return nil, fmt.Errorf("loading leaf template: %w", err)
}
return t, nil
}
func New(ctx context.Context, cfg *Config) (*Application, error) {
a := new(Application)
tmpls := []struct {
t **template.Template
name string
}{
{&a.indexTmpl, "index"},
{&a.notFoundTmpl, "404"},
}
for _, tmpl := range tmpls {
t, err := loadTemplate(cfg.Templates, tmpl.name)
if err != nil {
return nil, fmt.Errorf("loading template %q: %w", tmpl.name, err)
}
*tmpl.t = t
}
return a, nil
}