cerrado @ v0.4

 1package static
 2
 3import (
 4	"bytes"
 5	"fmt"
 6	"io"
 7	"io/fs"
 8	"mime"
 9	"net/http"
10	"path/filepath"
11
12	"git.gabrielgio.me/cerrado/pkg/ext"
13	"git.gabrielgio.me/cerrado/static"
14	"github.com/alecthomas/chroma/v2"
15	"github.com/alecthomas/chroma/v2/formatters/html"
16	"github.com/alecthomas/chroma/v2/styles"
17)
18
19func ServeStaticHandler() (ext.ErrorRequestHandler, error) {
20	staticFs, err := fs.Sub(static.Static, ".")
21	if err != nil {
22		return nil, err
23	}
24
25	return func(w http.ResponseWriter, r *ext.Request) error {
26		var (
27			f = r.PathValue("file")
28			e = filepath.Ext(f)
29			m = mime.TypeByExtension(e)
30		)
31		ext.SetMIME(w, m)
32		w.Header().Add("Cache-Control", "max-age=31536000")
33		http.ServeFileFS(w, r.Request, staticFs, f)
34		return nil
35	}, nil
36}
37
38func ServeStaticCSSHandler(lightTheme, darkTheme string) (ext.ErrorRequestHandler, error) {
39	var (
40		lightStyle = styles.Get(lightTheme)
41		darkStyle  = styles.Get(darkTheme)
42		formatter  = html.New(
43			html.WithCSSComments(false),
44		)
45	)
46
47	return func(w http.ResponseWriter, r *ext.Request) error {
48		ext.SetMIME(w, "text/css")
49		w.Header().Add("Cache-Control", "max-age=31536000")
50
51		// use buffer so this function can fail before writing to http.ResponseWriter
52		var buffer bytes.Buffer
53
54		var style *chroma.Style
55		style = darkStyle
56		buffer.Write([]byte("[data-bs-theme=\"dark\"] {\n"))
57		err := formatter.WriteCSS(&ws{&buffer}, style)
58		if err != nil {
59			return err
60		}
61		buffer.Write([]byte("}\n"))
62
63		style = lightStyle
64		buffer.Write([]byte("[data-bs-theme=\"light\"] {\n"))
65		err = formatter.WriteCSS(&ws{&buffer}, style)
66		if err != nil {
67			return err
68		}
69		buffer.Write([]byte("}"))
70
71		_, err = io.Copy(w, &buffer)
72		if err != nil {
73			return err
74		}
75
76		return nil
77	}, nil
78}
79
80type ws struct {
81	inner io.Writer
82}
83
84// This is very cursed, and rely on the fact that it writes every css rule at time.
85// it adds & to the begging so it can be nested by the ServeStaticCSSHandler.
86// This will allow the follow bootstrap data-bs-theme.
87func (w *ws) Write(p []byte) (n int, err error) {
88	return fmt.Fprintf(w.inner, "& %s", string(p))
89}