mirror of
https://git.sr.ht/~adnano/kiln
synced 2024-10-30 09:23:09 +00:00
202 lines
4.5 KiB
Go
202 lines
4.5 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"io/ioutil"
|
|
"os"
|
|
pathpkg "path"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// Dir represents a directory.
|
|
type Dir struct {
|
|
Title string // Directory title.
|
|
Content string // Directory index content.
|
|
Path string // Directory path.
|
|
Pages []*Page // Pages in this directory.
|
|
Dirs []*Dir // Subdirectories.
|
|
index *Page // The index page.
|
|
feeds map[string][]byte // Feeds.
|
|
|
|
inputExt string // input file extension
|
|
outputExt string // output file extension
|
|
templateExt string // template file extension
|
|
}
|
|
|
|
// NewDir returns a new Dir with the given path.
|
|
func NewDir(path string) *Dir {
|
|
if path == "" {
|
|
path = "/"
|
|
} else {
|
|
path = "/" + path + "/"
|
|
}
|
|
return &Dir{
|
|
Path: path,
|
|
feeds: map[string][]byte{},
|
|
}
|
|
}
|
|
|
|
// read reads from a directory and indexes the files and directories within it.
|
|
func (d *Dir) read(srcDir string, path string) error {
|
|
entries, err := ioutil.ReadDir(pathpkg.Join(srcDir, path))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, entry := range entries {
|
|
name := entry.Name()
|
|
// Ignore names that start with "_"
|
|
if strings.HasPrefix(name, "_") {
|
|
continue
|
|
}
|
|
path := pathpkg.Join(path, name)
|
|
if entry.IsDir() {
|
|
// Gather directory data
|
|
dir := NewDir(path)
|
|
dir.inputExt = d.inputExt
|
|
dir.outputExt = d.outputExt
|
|
dir.templateExt = d.templateExt
|
|
if err := dir.read(srcDir, path); err != nil {
|
|
return err
|
|
}
|
|
d.Dirs = append(d.Dirs, dir)
|
|
} else {
|
|
srcPath := pathpkg.Join(srcDir, path)
|
|
content, err := ioutil.ReadFile(srcPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if ext := pathpkg.Ext(name); ext == d.inputExt {
|
|
// Gather page data
|
|
if strings.TrimSuffix(name, ext) == "index" {
|
|
d.index = NewPage(d.Path, content)
|
|
d.Title = d.index.Title
|
|
d.Content = d.index.Content
|
|
} else {
|
|
d.Pages = append(d.Pages, NewPage(path, content))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// manipulate processes and manipulates the directory's contents.
|
|
func (d *Dir) manipulate(cfg *Config) error {
|
|
if d.templateExt != "" {
|
|
// Create index
|
|
if d.index != nil {
|
|
var b strings.Builder
|
|
tmpl := cfg.Templates.FindTemplate(d.Path, "index"+d.templateExt)
|
|
if err := tmpl.Execute(&b, d); err != nil {
|
|
return err
|
|
}
|
|
d.index.Content = b.String()
|
|
}
|
|
|
|
// Manipulate pages
|
|
for i := range d.Pages {
|
|
var b strings.Builder
|
|
tmpl := cfg.Templates.FindTemplate(d.Path, "page"+d.templateExt)
|
|
if err := tmpl.Execute(&b, d.Pages[i]); err != nil {
|
|
return err
|
|
}
|
|
d.Pages[i].Content = b.String()
|
|
}
|
|
}
|
|
|
|
// Feed represents a feed.
|
|
type Feed struct {
|
|
Title string // Feed title.
|
|
Path string // Feed path.
|
|
Updated time.Time // Last updated time.
|
|
Entries []*Page // Feed entries.
|
|
}
|
|
|
|
// Create feeds
|
|
if title, ok := cfg.Feeds[d.Path]; ok {
|
|
var b bytes.Buffer
|
|
feed := &Feed{
|
|
Title: title,
|
|
Path: d.Path,
|
|
Updated: time.Now(),
|
|
Entries: d.Pages,
|
|
}
|
|
tmpl := cfg.Templates.FindTemplate(d.Path, "atom.xml")
|
|
if err := tmpl.Execute(&b, feed); err != nil {
|
|
return err
|
|
}
|
|
d.feeds[pathpkg.Join(d.Path, "atom.xml")] = b.Bytes()
|
|
}
|
|
|
|
// Manipulate subdirectories
|
|
for _, d := range d.Dirs {
|
|
if err := d.manipulate(cfg); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Format represents an output format.
|
|
type Format interface {
|
|
Format(*Page) (path string, content []byte)
|
|
}
|
|
|
|
// write writes the Dir to the provided destination path.
|
|
func (d *Dir) write(dstDir string, format Format) error {
|
|
// Create the directory
|
|
dirPath := pathpkg.Join(dstDir, d.Path)
|
|
if err := os.MkdirAll(dirPath, 0755); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Write pages
|
|
for _, page := range d.Pages {
|
|
path, content := format.Format(page)
|
|
dstPath := pathpkg.Join(dstDir, path)
|
|
dir := pathpkg.Dir(dstPath)
|
|
os.MkdirAll(dir, 0755)
|
|
f, err := os.Create(dstPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err := f.Write(content); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Write the index file
|
|
if d.index != nil {
|
|
path, content := format.Format(d.index)
|
|
dstPath := pathpkg.Join(dstDir, path)
|
|
dir := pathpkg.Dir(dstPath)
|
|
os.MkdirAll(dir, 0755)
|
|
f, err := os.Create(dstPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err := f.Write(content); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Write subdirectories
|
|
for _, dir := range d.Dirs {
|
|
dir.write(dstDir, format)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// sort sorts the directory's pages by date.
|
|
func (d *Dir) sort() {
|
|
sort.Slice(d.Pages, func(i, j int) bool {
|
|
return d.Pages[i].Date.After(d.Pages[j].Date)
|
|
})
|
|
// Sort subdirectories
|
|
for _, d := range d.Dirs {
|
|
d.sort()
|
|
}
|
|
}
|