From 2929b4279c7a8128bd305290cc4187b6afb11cde Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Fri, 13 May 2022 11:47:29 -0600 Subject: Implement rendering Posts to html --- srv/src/post/renderer.go | 96 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 srv/src/post/renderer.go (limited to 'srv/src/post/renderer.go') 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) +} -- cgit v1.2.3