summaryrefslogtreecommitdiff
path: root/srv/src/post/renderer.go
diff options
context:
space:
mode:
Diffstat (limited to 'srv/src/post/renderer.go')
-rw-r--r--srv/src/post/renderer.go96
1 files changed, 96 insertions, 0 deletions
diff --git a/srv/src/post/renderer.go b/srv/src/post/renderer.go
new file mode 100644
index 0000000..74acc25
--- /dev/null
+++ b/srv/src/post/renderer.go
@@ -0,0 +1,96 @@
+package post
+
+import (
+ _ "embed"
+ "fmt"
+ "html/template"
+ "io"
+
+ "github.com/gomarkdown/markdown"
+ "github.com/gomarkdown/markdown/html"
+ "github.com/gomarkdown/markdown/parser"
+ "github.com/mediocregopher/blog.mediocregopher.com/srv/tpl"
+)
+
+// RenderablePost is a Post wrapped with extra information necessary for
+// rendering.
+type RenderablePost struct {
+ StoredPost
+ SeriesPrevious, SeriesNext *StoredPost
+}
+
+// NewRenderablePost wraps an existing Post such that it can be rendered.
+func NewRenderablePost(store Store, post StoredPost) (RenderablePost, error) {
+
+ renderablePost := RenderablePost{
+ StoredPost: post,
+ }
+
+ if post.Series != "" {
+
+ seriesPosts, err := store.GetBySeries(post.Series)
+ if err != nil {
+ return RenderablePost{}, fmt.Errorf(
+ "fetching posts for series %q: %w",
+ post.Series, err,
+ )
+ }
+
+ var foundThis bool
+
+ for i := range seriesPosts {
+
+ seriesPost := seriesPosts[i]
+
+ if seriesPost.ID == post.ID {
+ foundThis = true
+ continue
+ }
+
+ if !foundThis {
+ renderablePost.SeriesPrevious = &seriesPost
+ continue
+ }
+
+ renderablePost.SeriesNext = &seriesPost
+ break
+ }
+ }
+
+ return renderablePost, nil
+}
+
+// Renderer takes a Post and renders it to some encoding.
+type Renderer interface {
+ Render(io.Writer, RenderablePost) error
+}
+
+func mdBodyToHTML(body []byte) []byte {
+ parserExt := parser.CommonExtensions | parser.AutoHeadingIDs
+ parser := parser.NewWithExtensions(parserExt)
+
+ htmlFlags := html.CommonFlags | html.HrefTargetBlank
+ htmlRenderer := html.NewRenderer(html.RendererOptions{Flags: htmlFlags})
+
+ return markdown.ToHTML(body, parser, htmlRenderer)
+}
+
+type mdHTMLRenderer struct{}
+
+// NewMarkdownToHTMLRenderer renders Posts from markdown to HTML.
+func NewMarkdownToHTMLRenderer() Renderer {
+ return mdHTMLRenderer{}
+}
+
+func (r mdHTMLRenderer) Render(into io.Writer, post RenderablePost) error {
+
+ data := struct {
+ RenderablePost
+ Body template.HTML
+ }{
+ RenderablePost: post,
+ Body: template.HTML(mdBodyToHTML([]byte(post.Body))),
+ }
+
+ return tpl.HTML.ExecuteTemplate(into, "post.html", data)
+}