Initial commit: shared Go utilities (ssr, stringsx)

Extracted from sweeper to share across Go projects:
- ssr: template caching and SSR writer
- stringsx: string helpers
This commit is contained in:
soup 2026-01-13 10:43:04 -05:00
commit 173d775ae2
Signed by: soup
SSH key fingerprint: SHA256:GYxje8eQkJ6HZKzVWDdyOUF1TyDiprruGhE0Ym8qYDY
4 changed files with 87 additions and 0 deletions

71
ssr/ssr.go Normal file
View file

@ -0,0 +1,71 @@
package ssr
import (
"errors"
"html/template"
"io"
"sync"
"github.com/cespare/xxhash/v2"
)
type Writer struct {
err error
w io.Writer
cache *TmplCache
}
func NewWriter(w io.Writer, cache *TmplCache) *Writer {
return &Writer{w: w, cache: cache}
}
func (w *Writer) Raw(s string) error {
_, err := w.w.Write([]byte(s))
w.err = errors.Join(w.err, err)
return w.err
}
func (w *Writer) Tmpl(data any, tmpl string) error {
t, err := w.cache.GetOrCompile(tmpl)
if err != nil {
w.err = errors.Join(w.err, err)
return w.err
}
err = t.Execute(w.w, data)
w.err = errors.Join(w.err, err)
return w.err
}
func (w *Writer) Error() error {
return w.err
}
type TmplCache struct {
funcs template.FuncMap
cache sync.Map // uint64 -> *template.Template
}
func NewTmplCache(funcs template.FuncMap) *TmplCache {
return &TmplCache{funcs: funcs}
}
func (t *TmplCache) GetOrCompile(content string) (*template.Template, error) {
key := xxhash.Sum64String(content)
if cached, ok := t.cache.Load(key); ok {
return cached.(*template.Template), nil
}
tmpl, err := template.New("").Funcs(t.funcs).Parse(content)
if err != nil {
return nil, err
}
t.cache.Store(key, tmpl)
return tmpl, nil
}
type Renderable interface {
Render(sw *Writer) error
}