Implement Atom feed output

This commit is contained in:
adnano 2020-09-29 16:42:27 -04:00
parent bf6050c926
commit 531b92f191
3 changed files with 136 additions and 60 deletions

119
kiln.go
View file

@ -18,30 +18,25 @@ import (
// Site represents a kiln site. // Site represents a kiln site.
type Site struct { type Site struct {
Static map[string][]byte // Static files URL string // Site URL
Directory *Directory // Site directory Directory *Directory // Site directory
Templates *template.Template // Templates Templates *template.Template // Templates
} }
// LoadSite loads and returns a Site. // Load loads the Site from the specified source directory.
// It reads site content from the specified source directory. func (s *Site) Load(srcDir string) error {
func LoadSite(srcDir string) (*Site, error) { s.Directory = NewDirectory("")
tmpl, err := template.New("templates").ParseGlob("templates/*.gmi") tmpl, err := template.New("templates").ParseGlob("templates/*.gmi")
if err != nil { if err != nil {
return nil, err return err
} }
s.Templates = tmpl
site := &Site{ if err := site.Directory.Read(""); err != nil {
Static: map[string][]byte{}, return err
Directory: NewDirectory(""),
Templates: tmpl,
} }
return nil
if err := site.Directory.Read(site, srcDir, ""); err != nil {
return nil, err
}
return site, nil
} }
// Write writes the contents of the Index to the provided destination directory. // Write writes the contents of the Index to the provided destination directory.
@ -55,28 +50,6 @@ func (s *Site) Write(dstDir string, format OutputFormat) error {
return err return err
} }
// Write the static files
for path := range s.Static {
// Create any parent directories
if dir := filepath.Dir(path); dir != "." {
dstPath := filepath.Join(dstDir, dir)
if err := os.MkdirAll(dstPath, 0755); err != nil {
return err
}
}
// Write the file
dstPath := filepath.Join(dstDir, path)
f, err := os.Create(dstPath)
if err != nil {
return err
}
data := s.Static[path]
if _, err := f.Write(data); err != nil {
return err
}
}
// Write the directory // Write the directory
return s.Directory.Write(dstDir, format) return s.Directory.Write(dstDir, format)
} }
@ -88,7 +61,9 @@ func (s *Site) Manipulate(dir *Directory) error {
path := filepath.Join(dir.Permalink, "index.gmi") path := filepath.Join(dir.Permalink, "index.gmi")
var b bytes.Buffer var b bytes.Buffer
tmpl := s.Templates.Lookup("index.gmi") tmpl := s.Templates.Lookup("index.gmi")
if tmpl != nil { if tmpl == nil {
tmpl = indexTmpl
}
if err := tmpl.Execute(&b, dir); err != nil { if err := tmpl.Execute(&b, dir); err != nil {
return err return err
} }
@ -103,12 +78,14 @@ func (s *Site) Manipulate(dir *Directory) error {
} }
dir.Index = page dir.Index = page
} }
}
// Manipulate pages // Manipulate pages
for i := range dir.Pages { for i := range dir.Pages {
var b bytes.Buffer var b bytes.Buffer
tmpl := s.Templates.Lookup("page.gmi") tmpl := s.Templates.Lookup("page.gmi")
if tmpl == nil {
tmpl = pageTmpl
}
if err := tmpl.Execute(&b, dir.Pages[i]); err != nil { if err := tmpl.Execute(&b, dir.Pages[i]); err != nil {
return err return err
} }
@ -123,6 +100,37 @@ func (s *Site) Sort() {
s.Directory.Sort() s.Directory.Sort()
} }
type Feed struct {
Title string
Path string
SiteURL string
Updated time.Time
Entries []*Page
}
// CreateFeeds creates Atom feeds.
func (s *Site) CreateFeeds() error {
const atomPath = "atom.xml"
var b bytes.Buffer
tmpl := s.Templates.Lookup(atomPath)
if tmpl == nil {
tmpl = atomTmpl
}
feed := &Feed{
Title: "Example Feed",
Path: filepath.Join(s.Directory.Permalink, atomPath),
SiteURL: s.URL,
Updated: time.Now(),
Entries: s.Directory.Pages,
}
if err := tmpl.Execute(&b, feed); err != nil {
return err
}
path := filepath.Join(s.Directory.Permalink, atomPath)
s.Directory.Static[path] = b.Bytes()
return nil
}
// Page represents a page. // Page represents a page.
type Page struct { type Page struct {
// The permalink to this page. // The permalink to this page.
@ -198,6 +206,8 @@ type Directory struct {
Directories []*Directory Directories []*Directory
// The index file (index.gmi). // The index file (index.gmi).
Index *Page Index *Page
// Static files
Static map[string][]byte
} }
// NewDirectory returns a new Directory with the given path. // NewDirectory returns a new Directory with the given path.
@ -210,27 +220,29 @@ func NewDirectory(path string) *Directory {
} }
return &Directory{ return &Directory{
Permalink: permalink, Permalink: permalink,
Static: map[string][]byte{},
} }
} }
// Read reads from a directory and indexes the files and directories within it. // Read reads from a directory and indexes the files and directories within it.
func (d *Directory) Read(site *Site, srcDir string, path string) error { func (d *Directory) Read(path string) error {
entries, err := ioutil.ReadDir(srcDir) entries, err := ioutil.ReadDir(filepath.Join("src", path))
if err != nil { if err != nil {
return err return err
} }
for _, entry := range entries { for _, entry := range entries {
name := entry.Name() name := entry.Name()
path := filepath.Join(path, name)
srcPath := filepath.Join(srcDir, path)
if entry.IsDir() { if entry.IsDir() {
// Gather directory data // Gather directory data
path := filepath.Join(path, name)
dir := NewDirectory(path) dir := NewDirectory(path)
dir.Read(site, srcPath, path) if err := dir.Read(path); err != nil {
return err
}
d.Directories = append(d.Directories, dir) d.Directories = append(d.Directories, dir)
} else { } else {
srcPath := filepath.Join("src", path, name)
content, err := ioutil.ReadFile(srcPath) content, err := ioutil.ReadFile(srcPath)
if err != nil { if err != nil {
return err return err
@ -238,6 +250,7 @@ func (d *Directory) Read(site *Site, srcDir string, path string) error {
switch filepath.Ext(name) { switch filepath.Ext(name) {
case ".gmi", ".gemini": case ".gmi", ".gemini":
path := filepath.Join(path, name)
// Gather page data // Gather page data
page := NewPage(path, content) page := NewPage(path, content)
@ -249,7 +262,8 @@ func (d *Directory) Read(site *Site, srcDir string, path string) error {
default: default:
// Static file // Static file
site.Static[path] = content path := filepath.Join(path, name)
d.Static[path] = content
} }
} }
} }
@ -264,6 +278,19 @@ func (d *Directory) Write(dstDir string, format OutputFormat) error {
return err return err
} }
// Write static files
for path := range d.Static {
dstPath := filepath.Join(dstDir, path)
f, err := os.Create(dstPath)
if err != nil {
return err
}
data := d.Static[path]
if _, err := f.Write(data); err != nil {
return err
}
}
// Write the files // Write the files
for _, page := range d.Pages { for _, page := range d.Pages {
path, content := format(page) path, content := format(page)

12
main.go
View file

@ -11,11 +11,15 @@ import (
var ( var (
serveSite bool serveSite bool
toHtml bool toHtml bool
toAtom bool
site Site
) )
func init() { func init() {
flag.BoolVar(&serveSite, "serve", false, "serve the site") flag.BoolVar(&serveSite, "serve", false, "serve the site")
flag.BoolVar(&toHtml, "html", false, "output HTML") flag.BoolVar(&toHtml, "html", false, "output HTML")
flag.BoolVar(&toAtom, "atom", false, "output Atom feed")
flag.StringVar(&site.URL, "url", "", "site URL")
} }
func main() { func main() {
@ -31,14 +35,18 @@ func main() {
// build the site // build the site
func build() error { func build() error {
site, err := LoadSite("src") if err := site.Load("src"); err != nil {
if err != nil {
return err return err
} }
site.Sort() site.Sort()
if err := site.Manipulate(site.Directory); err != nil { if err := site.Manipulate(site.Directory); err != nil {
return err return err
} }
if toAtom {
if err := site.CreateFeeds(); err != nil {
return err
}
}
if err := site.Write("dst", OutputGemini); err != nil { if err := site.Write("dst", OutputGemini); err != nil {
return err return err
} }

41
templates.go Normal file
View file

@ -0,0 +1,41 @@
package main
import "text/template"
// Default templates
const atom_xml = `<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>{{ .Title }}</title>
<link href="{{ .SiteURL }}"/>
<link rel="self" href="{{ .SiteURL }}{{ .Path }}"/>
<updated>{{ .Updated }}</updated>
<id>{{ .SiteURL }}{{ .Path }}</id>
{{ $siteURL := .SiteURL }}
{{ range .Entries }}
<entry>
<title>{{ .Title }}</title>
<link href="{{ $siteURL }}{{ .Permalink }}"/>
<id>{{ $siteURL }}{{ .Permalink }}</id>
<updated>{{ .Date.Format "2006-01-02T15:04:05Z07:00" }}</updated>
<content src="{{ $siteURL }}{{ .Permalink }}" type="text/gemini"></content>
</entry>
{{ end }}
</feed>`
const index_gmi = `# Index of {{ .Permalink }}
{{ range .Directories }}=> {{ .Permalink }}
{{ end }}
{{ range .Pages }}=> {{ .Permalink }}
{{ end }}`
const page_gmi = `# {{ .Title }}
{{ .Content }}`
var (
atomTmpl = template.Must(template.New("atom.xml").Parse(atom_xml))
indexTmpl = template.Must(template.New("index.gmi").Parse(index_gmi))
pageTmpl = template.Must(template.New("page.gmi").Parse(page_gmi))
)