Atom feeds, date parsing in template, etc.
Anirudh Oppiliappan x@icyphox.sh
Thu, 05 Aug 2021 10:20:32 +0530
6 files changed,
144 insertions(+),
8 deletions(-)
A
atom/feed.go
@@ -0,0 +1,93 @@
+package atom + +import ( + "encoding/xml" + "time" + + "git.icyphox.sh/vite/config" + "git.icyphox.sh/vite/markdown" +) + +type AtomLink struct { + XMLName xml.Name `xml:"link"` + Href string `xml:"href,attr"` + Rel string `xml:"rel,attr,omitempty"` +} + +type AtomSummary struct { + XMLName xml.Name `xml:"summary"` + Content string `xml:",chardata"` + Type string `xml:"type,attr"` +} + +type AtomAuthor struct { + XMLName xml.Name `xml:"author"` + Name string `xml:"name"` + Email string `xml:"email"` +} + +type AtomEntry struct { + XMLName xml.Name `xml:"entry"` + Title string `xml:"title"` + Updated string `xml:"updated"` + ID string `xml:"id"` + Link *AtomLink + Summary *AtomSummary +} + +type AtomFeed struct { + XMLName xml.Name `xml:"feed"` + Xmlns string `xml:"xmlns,attr"` + Title string `xml:"title"` + Subtitle string `xml:"subtitle"` + ID string `xml:"id"` + Updated string `xml:"updated"` + Link *AtomLink + Author *AtomAuthor `xml:"author"` + Entries []AtomEntry +} + +func NewAtomFeed(srcDir string, posts []markdown.Output) ([]byte, error) { + entries := []AtomEntry{} + config := config.Config + for _, p := range posts { + dateStr := p.Meta["date"] + date, err := time.Parse("2006-01-02", dateStr) + if err != nil { + return nil, err + } + rfc3339 := date.Format(time.RFC3339) + + entry := AtomEntry{ + Title: p.Meta["title"], + Updated: rfc3339, + ID: NewUUID().String(), + Link: &AtomLink{Href: config.URL + srcDir + p.Meta["slug"]}, + Summary: &AtomSummary{Content: string(p.HTML), Type: "html"}, + } + entries = append(entries, entry) + } + + // 2021-07-14T00:00:00Z + now := time.Now().Format(time.RFC3339) + feed := &AtomFeed{ + Xmlns: "http://www.w3.org/2005/Atom", + Title: config.Title, + ID: config.URL, + Subtitle: config.Desc, + Link: &AtomLink{Href: config.URL}, + Author: &AtomAuthor{ + Name: config.Author.Name, + Email: config.Author.Email, + }, + Updated: now, + Entries: entries, + } + + feedXML, err := xml.MarshalIndent(feed, " ", " ") + if err != nil { + return nil, err + } + // Add the <?xml...> header. + return []byte(xml.Header + string(feedXML)), nil +}
A
atom/uuid.go
@@ -0,0 +1,25 @@
+package atom + +import ( + "crypto/rand" + "fmt" +) + +type UUID [16]byte + +// Create a new uuid v4 +func NewUUID() *UUID { + u := &UUID{} + _, err := rand.Read(u[:16]) + if err != nil { + panic(err) + } + + u[8] = (u[8] | 0x80) & 0xBf + u[6] = (u[6] | 0x40) & 0x4f + return u +} + +func (u *UUID) String() string { + return fmt.Sprintf("%x-%x-%x-%x-%x", u[:4], u[4:6], u[6:8], u[8:10], u[10:]) +}
M
commands/build.go
→
commands/build.go
@@ -7,6 +7,7 @@ "sort"
"strings" "time" + "git.icyphox.sh/vite/atom" "git.icyphox.sh/vite/config" "git.icyphox.sh/vite/markdown" "git.icyphox.sh/vite/util"@@ -137,8 +138,10 @@ }
// Sort posts slice by date sort.Slice(posts, func(i, j int) bool { - date1 := posts[j].Meta["date"].(time.Time) - date2 := posts[i].Meta["date"].(time.Time) + dateStr1 := posts[j].Meta["date"] + dateStr2 := posts[i].Meta["date"] + date1, _ := time.Parse("2006-01-02", dateStr1) + date2, _ := time.Parse("2006-01-02", dateStr2) return date1.Before(date2) }) }@@ -160,6 +163,15 @@ Meta markdown.Matter
Body string Posts []markdown.Output }{config.Config, out.Meta, string(out.HTML), posts}) + + // Create feeds + // ex: build/blog/feed.xml + xml, err := atom.NewAtomFeed(d, posts) + if err != nil { + return err + } + feedFile := filepath.Join(dstDir, "feed.xml") + os.WriteFile(feedFile, xml, 0755) } return nil }
M
commands/new.go
→
commands/new.go
@@ -14,7 +14,7 @@ url := strings.TrimSuffix(file, filepath.Ext(file))
content := fmt.Sprintf(`--- template: -url: %s +slug: %s title: subtitle: date: %s@@ -24,6 +24,6 @@ _, err := os.Create(path)
if err != nil { return err } - os.WriteFile(path, []byte(content), 0644) + os.WriteFile(path, []byte(content), 0755) return nil }
M
markdown/frontmatter.go
→
markdown/frontmatter.go
@@ -9,7 +9,7 @@
"gopkg.in/yaml.v3" ) -type Matter map[string]interface{} +type Matter map[string]string type MarkdownDoc struct { Frontmatter Matter
M
markdown/markdown.go
→
markdown/markdown.go
@@ -4,6 +4,7 @@ import (
"os" "path/filepath" "text/template" + "time" bfc "github.com/Depado/bfchroma" bf "github.com/russross/blackfriday/v2"@@ -46,11 +47,16 @@ // Renders out.HTML into dst html file, using the template specified
// in the frontmatter. data is the template struct. func (out *Output) RenderHTML(dst, tmplDir string, data interface{}) error { metaTemplate := out.Meta["template"] - if metaTemplate == nil { + if metaTemplate == "" { metaTemplate = "text.html" } - t, err := template.ParseGlob(filepath.Join(tmplDir, "*.html")) + t, err := template.New("").Funcs(template.FuncMap{ + "parsedate": func(s string) time.Time { + date, _ := time.Parse("2006-01-02", s) + return date + }, + }).ParseGlob(filepath.Join(tmplDir, "*.html")) if err != nil { return err }@@ -60,7 +66,7 @@ if err != nil {
return err } - if err = t.ExecuteTemplate(w, metaTemplate.(string), data); err != nil { + if err = t.ExecuteTemplate(w, metaTemplate, data); err != nil { return err } return nil