You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

203 lines
4.4 KiB

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.
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
if name == "index.gmi" {
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))
}
} 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 index
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.Content = b.String()
}
// Manipulate pages
for i := range d.Pages {
var b strings.Builder
tmpl := cfg.Templates.FindTemplate(d.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.
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.files[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
}
// 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 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)
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, 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()
}
}