Home

blog @c3f2ac928fbd41a4af9603d6b31a0bd234edaf23 - refs - log -
-
https://git.jolheiser.com/blog.git
My nonexistent blog
blog / templates.go
- raw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package main

import (
	"bytes"
	"fmt"
	"strings"
	"time"

	"github.com/BurntSushi/toml"
	chromahtml "github.com/alecthomas/chroma/v2/formatters/html"
	"github.com/yuin/goldmark"
	emoji "github.com/yuin/goldmark-emoji"
	highlighting "github.com/yuin/goldmark-highlighting/v2"
	meta "github.com/yuin/goldmark-meta"
	"github.com/yuin/goldmark/extension"
	"github.com/yuin/goldmark/parser"
	"github.com/yuin/goldmark/renderer/html"
)

// Articles by Category
type Articles map[string][]Article

type Article struct {
	Content string
	Meta
}

func (a Article) Slug() string {
	slug := strings.NewReplacer(" ", "-").Replace(strings.ToLower(a.Meta.Title))
	return fmt.Sprintf("%s/%s/", a.Meta.Date.Format("2006"), slug)
}

type Meta struct {
	Title    string
	Summary  string
	Date     time.Time
	Category string
}

type Author struct {
	Name  string
	Email string
}

func Parse(content string) (Article, error) {
	lines := strings.Split(content, "\n")

	start, end := -1, -1
	for idx, line := range lines {
		if strings.TrimSpace(line) == "" {
			continue
		}

		if isTOMLSeparator(line) {
			if start == -1 {
				start = idx
				continue
			}
			end = idx
			break
		}
	}

	var meta Meta
	body := content
	if start != -1 && end != -1 {
		body = strings.Join(lines[end+1:], "\n")
		if err := toml.Unmarshal([]byte(strings.Join(lines[start+1:end], "\n")), &meta); err != nil {
			return Article{}, fmt.Errorf("could not parse frontmatter: %w", err)
		}
	}

	var buf bytes.Buffer
	err := Markdown.Convert([]byte(body), &buf)
	if err != nil {
		return Article{}, fmt.Errorf("could not convert article: %w", err)
	}

	return Article{
		Content: buf.String(),
		Meta:    meta,
	}, nil
}

func isTOMLSeparator(line string) bool {
	for _, char := range line {
		if char != '+' {
			return false
		}
	}
	return true
}

var (
	opts = []chromahtml.Option{
		chromahtml.WithClasses(true),
		chromahtml.WithAllClasses(true),
		// chromahtml.WithLineNumbers(true),
	}
	Markdown = goldmark.New(
		goldmark.WithParserOptions(
			parser.WithAutoHeadingID(),
		),
		goldmark.WithRendererOptions(
			html.WithUnsafe(),
		),
		goldmark.WithExtensions(
			extension.GFM,
			extension.Footnote,
			meta.Meta,
			emoji.Emoji,
			highlighting.NewHighlighting(
				highlighting.WithFormatOptions(
					opts...,
				),
			),
		),
	)
	CSS = chromahtml.New(opts...)
)