mirror of
https://git.sr.ht/~adnano/kiln
synced 2024-11-08 10:09:18 +01:00
202 lines
4.3 KiB
Go
202 lines
4.3 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"io/ioutil"
|
||
|
"os"
|
||
|
pathpkg "path"
|
||
|
"sort"
|
||
|
"strings"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
// Dir represents a directory.
|
||
|
type Dir struct {
|
||
|
Path string // Directory path.
|
||
|
Pages []*Page // Pages in this directory.
|
||
|
Dirs []*Dir // Subdirectories.
|
||
|
files map[string][]byte // Static files.
|
||
|
index *Page // The index page.
|
||
|
}
|
||
|
|
||
|
// NewDir returns a new Dir with the given path.
|
||
|
func NewDir(path string) *Dir {
|
||
|
if path == "" {
|
||
|
path = "/"
|
||
|
} else {
|
||
|
path = "/" + path + "/"
|
||
|
}
|
||
|
return &Dir{
|
||
|
Path: path,
|
||
|
files: 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)
|
||
|
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 pathpkg.Ext(name) == ".gmi" {
|
||
|
// Gather page data
|
||
|
page := NewPage(path, content)
|
||
|
if name == "index.gmi" {
|
||
|
d.index = page
|
||
|
} else {
|
||
|
d.Pages = append(d.Pages, page)
|
||
|
}
|
||
|
} else {
|
||
|
// Static file
|
||
|
d.files[path] = content
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// manipulate processes and manipulates the directory's contents.
|
||
|
func (d *Dir) manipulate(cfg *Config) error {
|
||
|
// Create the directory index file, if it doesn't exist
|
||
|
if d.index == nil {
|
||
|
var b strings.Builder
|
||
|
tmpl := cfg.Templates.FindTemplate(d.Path, "index.gmi")
|
||
|
if err := tmpl.Execute(&b, d); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
d.index = &Page{
|
||
|
Path: d.Path,
|
||
|
Content: b.String(),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Manipulate pages
|
||
|
for i := range d.Pages {
|
||
|
var b strings.Builder
|
||
|
tmpl := cfg.Templates.FindTemplate(d.Pages[i].Path, "page.gmi")
|
||
|
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.
|
||
|
Updated time.Time // Last updated time.
|
||
|
Entries []*Page // Feed entries.
|
||
|
}
|
||
|
|
||
|
// Create feeds
|
||
|
if title, ok := cfg.Feeds[d.Path]; ok {
|
||
|
var b strings.Builder
|
||
|
feed := &Feed{
|
||
|
Title: title,
|
||
|
Updated: time.Now(),
|
||
|
Entries: d.Pages,
|
||
|
}
|
||
|
tmpl := cfg.Templates.FindTemplate(d.Path, "feed.gmi")
|
||
|
if err := tmpl.Execute(&b, feed); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
d.Pages = append(d.Pages, &Page{
|
||
|
Path: pathpkg.Join(d.Path, "feed"),
|
||
|
Content: b.String(),
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// Manipulate subdirectories
|
||
|
for _, d := range d.Dirs {
|
||
|
if err := d.manipulate(cfg); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// write writes the Dir to the provided destination path.
|
||
|
func (d *Dir) write(dstDir string, format outputFormat, cfg *Config) error {
|
||
|
// Create the directory
|
||
|
dirPath := pathpkg.Join(dstDir, d.Path)
|
||
|
if err := os.MkdirAll(dirPath, 0755); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// Write static files
|
||
|
for path := range d.files {
|
||
|
dstPath := pathpkg.Join(dstDir, path)
|
||
|
f, err := os.Create(dstPath)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
data := d.files[path]
|
||
|
if _, err := f.Write(data); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Write the pages
|
||
|
for _, page := range d.Pages {
|
||
|
path, content := format(page, cfg)
|
||
|
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(d.index, cfg)
|
||
|
dstPath := pathpkg.Join(dstDir, path)
|
||
|
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, cfg)
|
||
|
}
|
||
|
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()
|
||
|
}
|
||
|
}
|