summaryrefslogtreecommitdiff
path: root/src/gmi/tpl.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/gmi/tpl.go')
-rw-r--r--src/gmi/tpl.go134
1 files changed, 134 insertions, 0 deletions
diff --git a/src/gmi/tpl.go b/src/gmi/tpl.go
new file mode 100644
index 0000000..7aa1a2f
--- /dev/null
+++ b/src/gmi/tpl.go
@@ -0,0 +1,134 @@
+package gmi
+
+import (
+ "bytes"
+ "context"
+ "embed"
+ "fmt"
+ "io"
+ "io/fs"
+ "net/url"
+ "strconv"
+ "strings"
+ "text/template"
+
+ "git.sr.ht/~adnano/go-gemini"
+ "github.com/mediocregopher/blog.mediocregopher.com/srv/post"
+ "github.com/mediocregopher/mediocre-go-lib/v2/mctx"
+)
+
+//go:embed tpl
+var tplFS embed.FS
+
+type rendererGetPostsRes struct {
+ Posts []post.StoredPost
+ HasMore bool
+}
+
+type renderer struct {
+ url *url.URL
+ postStore post.Store
+}
+
+func (r renderer) GetPosts(page, count int) (rendererGetPostsRes, error) {
+ posts, hasMore, err := r.postStore.Get(page, count)
+ return rendererGetPostsRes{posts, hasMore}, err
+}
+
+func (r renderer) GetPostByID(id string) (post.StoredPost, error) {
+ p, err := r.postStore.GetByID(id)
+ if err != nil {
+ return post.StoredPost{}, fmt.Errorf("fetching post %q: %w", id, err)
+ }
+ return p, nil
+}
+
+func (r renderer) GetQueryValue(key, def string) string {
+ v := r.url.Query().Get(key)
+ if v == "" {
+ v = def
+ }
+ return v
+}
+
+func (r renderer) GetQueryIntValue(key string, def int) (int, error) {
+ vStr := r.GetQueryValue(key, strconv.Itoa(def))
+ return strconv.Atoi(vStr)
+}
+
+func (r renderer) Add(a, b int) int { return a + b }
+
+func (a *api) tplHandler() (gemini.Handler, error) {
+
+ allTpls := template.New("")
+
+ err := fs.WalkDir(tplFS, "tpl", func(path string, d fs.DirEntry, err error) error {
+
+ if err != nil {
+ return err
+ }
+
+ if d.IsDir() {
+ return nil
+ }
+
+ body, err := fs.ReadFile(tplFS, path)
+ if err != nil {
+ panic(err)
+ }
+
+ name := strings.TrimPrefix(path, "tpl/")
+
+ allTpls, err = allTpls.New(name).Parse(string(body))
+ if err != nil {
+ return fmt.Errorf("parsing %q as template: %w", path, err)
+ }
+
+ return nil
+ })
+
+ if err != nil {
+ return nil, fmt.Errorf("parsing templates: %w", err)
+ }
+
+ return gemini.HandlerFunc(func(
+ ctx context.Context,
+ rw gemini.ResponseWriter,
+ r *gemini.Request,
+ ) {
+
+ if strings.HasSuffix(r.URL.Path, "/") {
+ r.URL.Path += "index.gmi"
+ }
+
+ tplPath := strings.TrimPrefix(r.URL.Path, "/")
+
+ ctx = mctx.Annotate(ctx,
+ "url", r.URL,
+ "tplPath", tplPath,
+ )
+
+ tpl := allTpls.Lookup(tplPath)
+
+ if tpl == nil {
+ a.params.Logger.WarnString(ctx, "page not found")
+ rw.WriteHeader(gemini.StatusNotFound, "Page not found, sorry!")
+ return
+ }
+
+ buf := new(bytes.Buffer)
+
+ err := tpl.Execute(buf, renderer{
+ url: r.URL,
+ postStore: a.params.PostStore,
+ })
+
+ if err != nil {
+ a.params.Logger.Error(ctx, "rendering error", err)
+ rw.WriteHeader(gemini.StatusTemporaryFailure, err.Error())
+ return
+ }
+
+ io.Copy(rw, buf)
+ }), nil
+}