summaryrefslogtreecommitdiff
path: root/src/gmi
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
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')
-rw-r--r--src/gmi/gemtext/gemtext.go (renamed from src/gmi/gemtext.go)8
-rw-r--r--src/gmi/gemtext/gemtext_test.go (renamed from src/gmi/gemtext_test.go)6
-rw-r--r--src/gmi/gmi.go14
-rw-r--r--src/gmi/tpl.go150
-rw-r--r--src/gmi/tpl/posts/post.gmi4
5 files changed, 45 insertions, 137 deletions
diff --git a/src/gmi/gemtext.go b/src/gmi/gemtext/gemtext.go
index 884635c..5c8f594 100644
--- a/src/gmi/gemtext.go
+++ b/src/gmi/gemtext/gemtext.go
@@ -1,4 +1,6 @@
-package gmi
+// Package gemtext contains code related to processing and producing gemtext
+// documents.
+package gemtext
import (
"bufio"
@@ -23,12 +25,12 @@ func hasImgExt(p string) bool {
// matches `=> dstURL [optional description]`
var linkRegexp = regexp.MustCompile(`^=>\s+(\S+)\s*(.*?)\s*$`)
-// GemtextToMarkdown reads a gemtext formatted body from the Reader and writes
+// ToMarkdown reads a gemtext formatted body from the Reader and writes
// the markdown version of that body to the Writer.
//
// gmiGateway, if given, is used for all `gemini://` links. The `gemini://`
// prefix will be stripped, and replaced with the given URL.
-func GemtextToMarkdown(dst io.Writer, src io.Reader, gmiGateway *url.URL) error {
+func ToMarkdown(dst io.Writer, src io.Reader, gmiGateway *url.URL) error {
bufSrc := bufio.NewReader(src)
diff --git a/src/gmi/gemtext_test.go b/src/gmi/gemtext/gemtext_test.go
index 75da9df..fe58a64 100644
--- a/src/gmi/gemtext_test.go
+++ b/src/gmi/gemtext/gemtext_test.go
@@ -1,4 +1,4 @@
-package gmi
+package gemtext
import (
"bytes"
@@ -9,7 +9,7 @@ import (
"github.com/stretchr/testify/assert"
)
-func TestGemtextToMarkdown(t *testing.T) {
+func TestToMarkdown(t *testing.T) {
gmiGateway, _ := url.Parse("https://gateway.com/x/")
@@ -58,7 +58,7 @@ func TestGemtextToMarkdown(t *testing.T) {
t.Run(strconv.Itoa(i), func(t *testing.T) {
got := new(bytes.Buffer)
- err := GemtextToMarkdown(got, bytes.NewBufferString(test.in), gmiGateway)
+ err := ToMarkdown(got, bytes.NewBufferString(test.in), gmiGateway)
assert.NoError(t, err)
assert.Equal(t, test.exp, got.String())
})
diff --git a/src/gmi/gmi.go b/src/gmi/gmi.go
index 467ab5a..e37ca74 100644
--- a/src/gmi/gmi.go
+++ b/src/gmi/gmi.go
@@ -14,14 +14,14 @@ import (
"path/filepath"
"strings"
- "git.sr.ht/~adnano/go-gemini"
- "git.sr.ht/~adnano/go-gemini/certificate"
"dev.mediocregopher.com/mediocre-blog.git/src/cache"
"dev.mediocregopher.com/mediocre-blog.git/src/cfg"
"dev.mediocregopher.com/mediocre-blog.git/src/post"
"dev.mediocregopher.com/mediocre-blog.git/src/post/asset"
"dev.mediocregopher.com/mediocre-go-lib.git/mctx"
"dev.mediocregopher.com/mediocre-go-lib.git/mlog"
+ "git.sr.ht/~adnano/go-gemini"
+ "git.sr.ht/~adnano/go-gemini/certificate"
)
// Params are used to instantiate a new API instance. All fields are required
@@ -37,7 +37,8 @@ type Params struct {
ListenAddr string
CertificatesPath string
- HTTPPublicURL *url.URL
+ HTTPPublicURL *url.URL
+ HTTPGeminiGatewayURL *url.URL
}
// SetupCfg implement the cfg.Cfger interface.
@@ -193,12 +194,7 @@ func postsMiddleware(tplHandler gemini.Handler) gemini.Handler {
return
}
- query := r.URL.Query()
- query.Set("id", id)
- r.URL.RawQuery = query.Encode()
-
- r.URL.Path = "/posts/post.gmi"
-
+ ctx = withTplPath(ctx, "/posts/post.gmi")
tplHandler.ServeGemini(ctx, rw, r)
})
}
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
diff --git a/src/gmi/tpl/posts/post.gmi b/src/gmi/tpl/posts/post.gmi
index 0234395..b568044 100644
--- a/src/gmi/tpl/posts/post.gmi
+++ b/src/gmi/tpl/posts/post.gmi
@@ -1,4 +1,4 @@
-{{ $post := .GetPostByID (.GetQueryValue "id" "") -}}
+{{ $post := .GetThisPost -}}
{{ if eq $post.Format "md" -}}
This post has been translated from it's original markdown format, if it seems busted it might appear better over HTTP:
@@ -14,7 +14,7 @@ This post has been translated from it's original markdown format, if it seems bu
{{ end -}}
-{{ .PostBody $post }}
+{{ .PostGemtextBody $post }}
========================================