mirror of
https://git.sr.ht/~adnano/kiln
synced 2024-10-30 09:23:09 +00:00
Implement Atom feed output
This commit is contained in:
parent
bf6050c926
commit
531b92f191
143
kiln.go
143
kiln.go
|
@ -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,27 +61,31 @@ 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 {
|
||||||
if err := tmpl.Execute(&b, dir); err != nil {
|
tmpl = indexTmpl
|
||||||
return err
|
|
||||||
}
|
|
||||||
content := b.Bytes()
|
|
||||||
permalink := filepath.Dir(path)
|
|
||||||
if permalink == "." {
|
|
||||||
permalink = ""
|
|
||||||
}
|
|
||||||
page := &Page{
|
|
||||||
Permalink: "/" + permalink,
|
|
||||||
content: content,
|
|
||||||
}
|
|
||||||
dir.Index = page
|
|
||||||
}
|
}
|
||||||
|
if err := tmpl.Execute(&b, dir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
content := b.Bytes()
|
||||||
|
permalink := filepath.Dir(path)
|
||||||
|
if permalink == "." {
|
||||||
|
permalink = ""
|
||||||
|
}
|
||||||
|
page := &Page{
|
||||||
|
Permalink: "/" + permalink,
|
||||||
|
content: content,
|
||||||
|
}
|
||||||
|
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
12
main.go
|
@ -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
41
templates.go
Normal 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))
|
||||||
|
)
|
Loading…
Reference in a new issue