fast and minimal static site generator
ssg
1package atom
2
3import (
4 "encoding/xml"
5 "fmt"
6 "net/url"
7 "path/filepath"
8 "time"
9
10 "tangled.sh/icyphox.sh/vite/config"
11 "tangled.sh/icyphox.sh/vite/types"
12)
13
14type AtomLink struct {
15 XMLName xml.Name `xml:"link"`
16 Href string `xml:"href,attr"`
17 Rel string `xml:"rel,attr,omitempty"`
18}
19
20type AtomSummary struct {
21 XMLName xml.Name `xml:"summary"`
22 Content string `xml:",chardata"`
23 Type string `xml:"type,attr"`
24}
25
26type AtomAuthor struct {
27 XMLName xml.Name `xml:"author"`
28 Name string `xml:"name"`
29 Email string `xml:"email"`
30}
31
32type AtomEntry struct {
33 XMLName xml.Name `xml:"entry"`
34 Title string `xml:"title"`
35 Updated string `xml:"updated"`
36 ID string `xml:"id"`
37 Link *AtomLink
38 Summary *AtomSummary
39}
40
41type AtomFeed struct {
42 XMLName xml.Name `xml:"feed"`
43 Xmlns string `xml:"xmlns,attr"`
44 Title string `xml:"title"`
45 Subtitle string `xml:"subtitle"`
46 ID string `xml:"id"`
47 Updated string `xml:"updated"`
48 Link *AtomLink
49 Author *AtomAuthor `xml:"author"`
50 Entries []AtomEntry
51}
52
53// Creates a new Atom feed.
54func NewAtomFeed(srcDir string, posts []types.Post) ([]byte, error) {
55 entries := []AtomEntry{}
56
57 for _, p := range posts {
58 dateStr := p.Meta["date"].(string)
59 date, err := time.Parse("2006-01-02", dateStr)
60 if err != nil {
61 return nil, err
62 }
63 rfc3339 := date.Format(time.RFC3339)
64
65 var summaryContent string
66 if subtitle, ok := p.Meta["subtitle"]; ok {
67 summaryContent = fmt.Sprintf("<h2>%s</h2>\n%s",
68 subtitle.(string),
69 string(p.Body))
70 } else {
71 summaryContent = string(p.Body)
72 }
73
74 entry := AtomEntry{
75 Title: p.Meta["title"].(string),
76 Updated: rfc3339,
77 // tag:icyphox.sh,2019-10-23:blog/some-post/
78 ID: fmt.Sprintf(
79 "tag:%s,%s:%s",
80 config.Config.URL[8:], // strip https://
81 dateStr,
82 filepath.Join(srcDir, p.Meta["slug"].(string)),
83 ),
84 Link: newAtomLink(config.Config.URL, srcDir, p.Meta["slug"].(string)),
85 Summary: &AtomSummary{
86 Content: summaryContent,
87 Type: "html",
88 },
89 }
90 entries = append(entries, entry)
91 }
92
93 // 2021-07-14T00:00:00Z
94 now := time.Now().Format(time.RFC3339)
95 feed := &AtomFeed{
96 Xmlns: "http://www.w3.org/2005/Atom",
97 Title: config.Config.Title,
98 ID: config.Config.URL,
99 Subtitle: config.Config.Desc,
100 Link: &AtomLink{Href: config.Config.URL},
101 Author: &AtomAuthor{
102 Name: config.Config.Author.Name,
103 Email: config.Config.Author.Email,
104 },
105 Updated: now,
106 Entries: entries,
107 }
108
109 feedXML, err := xml.MarshalIndent(feed, " ", " ")
110 if err != nil {
111 return nil, err
112 }
113 // Add the <?xml...> header.
114 return []byte(xml.Header + string(feedXML)), nil
115}
116
117func newAtomLink(base, subDir, slug string) *AtomLink {
118 baseURL, err := url.Parse(base)
119 if err != nil {
120 return nil
121 }
122
123 baseURL.Path = filepath.Join(subDir, slug)
124 return &AtomLink{Href: baseURL.String()}
125}