commit 173d775ae2616e2337f417fb0611f2d03e9a7562 Author: soup Date: Tue Jan 13 10:43:04 2026 -0500 Initial commit: shared Go utilities (ssr, stringsx) Extracted from sweeper to share across Go projects: - ssr: template caching and SSR writer - stringsx: string helpers diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..c8e77b7 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module sxgo + +go 1.25.5 + +require github.com/cespare/xxhash/v2 v2.3.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..1987830 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= diff --git a/ssr/ssr.go b/ssr/ssr.go new file mode 100644 index 0000000..1e0cce9 --- /dev/null +++ b/ssr/ssr.go @@ -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 +} diff --git a/stringsx/stringsx.go b/stringsx/stringsx.go new file mode 100644 index 0000000..d079e43 --- /dev/null +++ b/stringsx/stringsx.go @@ -0,0 +1,9 @@ +// Package stringsx provides string-related helpers. +package stringsx + +import "strings" + +// IsBlank reports whether a string contains only whitespace. +func IsBlank(s string) bool { + return strings.TrimSpace(s) == "" +}