summaryrefslogtreecommitdiff
path: root/src/gmi/tpl.go
diff options
context:
space:
mode:
authorBrian Picciano <mediocregopher@gmail.com>2024-05-17 23:37:43 +0200
committerBrian Picciano <mediocregopher@gmail.com>2024-05-18 14:47:09 +0200
commit8d7e708d98a3a46ba3ba08f9c8deeb4838bb8ca5 (patch)
tree6662c3e4c6c3baaea058a3deaba0d9cfc8e9cc40 /src/gmi/tpl.go
parentfac06df97a47cda6e8989bfc5f40f2a627279b92 (diff)
Render posts completely using common rendering methods
The aim is to reduce reliance on custom logic in the handlers for every protocol, eventually outsourcing all of it into `render.Methods`, leaving each protocol to simply direct calls to the correct template.
Diffstat (limited to 'src/gmi/tpl.go')
-rw-r--r--src/gmi/tpl.go150
1 files changed, 30 insertions, 120 deletions
diff --git a/src/gmi/tpl.go b/src/gmi/tpl.go
index 03d9819..8ffa6bc 100644
--- a/src/gmi/tpl.go
+++ b/src/gmi/tpl.go
@@ -4,6 +4,7 @@ import (
"bytes"
"context"
"embed"
+ "errors"
"fmt"
"io"
"io/fs"
@@ -11,126 +12,27 @@ import (
"net/url"
"path"
"path/filepath"
- "strconv"
"strings"
"text/template"
- "git.sr.ht/~adnano/go-gemini"
"dev.mediocregopher.com/mediocre-blog.git/src/post"
+ "dev.mediocregopher.com/mediocre-blog.git/src/render"
"dev.mediocregopher.com/mediocre-go-lib.git/mctx"
- gmnhg "github.com/tdemin/gmnhg"
+ "git.sr.ht/~adnano/go-gemini"
)
-//go:embed tpl
-var tplFS embed.FS
-
-type rendererGetPostsRes struct {
- Posts []post.StoredPost
- HasMore bool
-}
-
-type rendererGetPostSeriesNextPreviousRes struct {
- Next *post.StoredPost
- Previous *post.StoredPost
-}
-
-type renderer struct {
- url *url.URL
- publicURL *url.URL
- postStore post.Store
- preprocessFuncs post.PreprocessFunctions
-}
-
-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) GetPostSeriesNextPrevious(p post.StoredPost) (rendererGetPostSeriesNextPreviousRes, error) {
-
- seriesPosts, err := r.postStore.GetBySeries(p.Series)
- if err != nil {
- return rendererGetPostSeriesNextPreviousRes{}, fmt.Errorf(
- "fetching posts for series %q: %w", p.Series, err,
- )
- }
-
- var (
- res rendererGetPostSeriesNextPreviousRes
- foundThis bool
- )
-
- for i := range seriesPosts {
-
- seriesPost := seriesPosts[i]
-
- if seriesPost.ID == p.ID {
- foundThis = true
- continue
- }
-
- if !foundThis {
- res.Next = &seriesPost
- continue
- }
-
- res.Previous = &seriesPost
- break
- }
-
- return res, nil
-}
-
-func (r renderer) PostBody(p post.StoredPost) (string, error) {
-
- buf := new(bytes.Buffer)
-
- if err := p.PreprocessBody(buf, r.preprocessFuncs); err != nil {
- return "", fmt.Errorf("preprocessing post body: %w", err)
- }
-
- bodyBytes := buf.Bytes()
-
- if p.Format == post.FormatMarkdown {
-
- gemtextBodyBytes, err := gmnhg.RenderMarkdown(bodyBytes, 0)
- if err != nil {
- return "", fmt.Errorf("converting from markdown: %w", err)
- }
-
- bodyBytes = gemtextBodyBytes
- }
-
- return string(bodyBytes), nil
-}
+type ctxKey string
-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)
-}
+const (
+ ctxKeyTplPath ctxKey = "tplPath"
+)
-func (r renderer) GetPath() (string, error) {
- basePath := filepath.Join("/", r.publicURL.Path) // in case it's empty
- return filepath.Rel(basePath, r.url.Path)
+func withTplPath(ctx context.Context, path string) context.Context {
+ return context.WithValue(ctx, ctxKeyTplPath, path)
}
-func (r renderer) Add(a, b int) int { return a + b }
+//go:embed tpl
+var tplFS embed.FS
func (a *api) tplHandler() (gemini.Handler, error) {
@@ -177,7 +79,6 @@ func (a *api) tplHandler() (gemini.Handler, error) {
return blogURL(a.params.HTTPPublicURL, path, true)
},
Image: func(args ...string) (string, error) {
-
var (
id = args[0]
descr = "Image"
@@ -243,9 +144,13 @@ func (a *api) tplHandler() (gemini.Handler, error) {
rw gemini.ResponseWriter,
r *gemini.Request,
) {
+ tplPath, _ := ctx.Value(ctxKeyTplPath).(string)
+ if tplPath == "" {
+ tplPath = r.URL.Path
+ }
+ tplPath = strings.TrimPrefix(tplPath, "/")
- tplPath := strings.TrimPrefix(r.URL.Path, "/")
- mimeType := mime.TypeByExtension(path.Ext(r.URL.Path))
+ mimeType := mime.TypeByExtension(path.Ext(tplPath))
ctx = mctx.Annotate(ctx,
"url", r.URL,
@@ -266,14 +171,19 @@ func (a *api) tplHandler() (gemini.Handler, error) {
buf := new(bytes.Buffer)
- err := tpl.Execute(buf, renderer{
- url: r.URL,
- publicURL: a.params.PublicURL,
- postStore: a.params.PostStore,
- preprocessFuncs: preprocessFuncs,
- })
-
- if err != nil {
+ err := tpl.Execute(buf, render.NewMethods(
+ ctx,
+ r.URL,
+ a.params.PublicURL,
+ a.params.HTTPGeminiGatewayURL,
+ a.params.PostStore,
+ preprocessFuncs,
+ ))
+
+ if errors.Is(err, post.ErrPostNotFound) {
+ a.params.Logger.Warn(ctx, "post not found", err)
+ rw.WriteHeader(gemini.StatusNotFound, "Post not found")
+ } else if err != nil {
a.params.Logger.Error(ctx, "rendering error", err)
rw.WriteHeader(gemini.StatusTemporaryFailure, err.Error())
return