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)
}