aboutsummaryrefslogtreecommitdiff
path: root/cmd/site/internal/blog/blog.go
blob: 2a63ce1b3bbfb66217758206a56d9818340bed26 (plain)
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package blog

import (
	"html/template"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"christine.website/cmd/site/internal/front"
	"github.com/russross/blackfriday"
)

// Post is a single blogpost.
type Post struct {
	Title      string        `json:"title"`
	Link       string        `json:"link"`
	Summary    string        `json:"summary,omitifempty"`
	Body       string        `json:"body_markdown"`
	BodyHTML   template.HTML `json:"body,omitempty"`
	Series     string        `json:"series,omitempty"`
	Tags       []string      `json:"tags,omitempty"`
	SlidesLink string        `json:"slides_link,omitempty"`
	ImageURL   string        `json:"image_url,omitempty"`
	ThumbURL   string        `json:"thumb_url,omitempty"`
	Date       time.Time
	DateString string `json:"date"`
}

// Posts implements sort.Interface for a slice of Post objects.
type Posts []Post

func (p Posts) Series() []string {
	names := map[string]struct{}{}

	for _, ps := range p {
		if ps.Series != "" {
			names[ps.Series] = struct{}{}
		}
	}

	var result []string

	for name := range names {
		result = append(result, name)
	}

	return result
}

func (p Posts) Len() int { return len(p) }
func (p Posts) Less(i, j int) bool {
	iDate := p[i].Date
	jDate := p[j].Date

	return iDate.Unix() < jDate.Unix()
}
func (p Posts) Swap(i, j int) { p[i], p[j] = p[j], p[i] }

// LoadPosts loads posts for a given directory.
func LoadPosts(path string, prepend string) (Posts, error) {
	type postFM struct {
		Title      string
		Date       string
		Series     string
		Tags       []string
		SlidesLink string `yaml:"slides_link"`
		Image      string
		Thumb      string
	}
	var result Posts

	err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() {
			return nil
		}

		fin, err := os.Open(path)
		if err != nil {
			return err
		}
		defer fin.Close()

		content, err := ioutil.ReadAll(fin)
		if err != nil {
			return err
		}

		var fm postFM
		remaining, err := front.Unmarshal(content, &fm)
		if err != nil {
			return err
		}

		output := blackfriday.Run(remaining)

		const timeFormat = `2006-01-02`
		date, err := time.Parse(timeFormat, fm.Date)
		if err != nil {
			return err
		}

		fname := filepath.Base(path)
		fname = strings.TrimSuffix(fname, filepath.Ext(fname))

		p := Post{
			Title:      fm.Title,
			Date:       date,
			DateString: fm.Date,
			Link:       filepath.Join(prepend, fname),
			Body:       string(remaining),
			BodyHTML:   template.HTML(output),
			SlidesLink: fm.SlidesLink,
			Series:     fm.Series,
			Tags:       fm.Tags,
			ImageURL:   fm.Image,
			ThumbURL:   fm.Thumb,
		}
		result = append(result, p)

		return nil
	})
	if err != nil {
		return nil, err
	}

	sort.Sort(sort.Reverse(result))

	return result, nil
}