~adnano/kiln

45d2f0e4cb570407f80eefcda83f9cb7bc0e42c0 — Adnan Maolood a month ago c0fcac8
Merge Page and Dir structs
4 files changed, 103 insertions(+), 130 deletions(-)

M funcs.go
M main.go
R dir.go => page.go
M site.go
M funcs.go => funcs.go +0 -1
@@ 10,7 10,6 @@ import (
// funcs returns functions for use in templates.
func (s *Site) funcs() map[string]interface{} {
	return map[string]interface{}{
		"dir":          s.dir,
		"exec":         executeString,
		"page":         s.page,
		"path":         func() _path { return _path{} },

M main.go => main.go +1 -1
@@ 71,7 71,7 @@ func (site *Site) run() error {

func (s *Site) runTask(task *Task) error {
	// Read content
	s.root = &Dir{Permalink: "/", path: ""}
	s.root = &Page{Permalink: "/", FilePath: ""}
	if err := s.root.read("content", task, s); err != nil {
		return err
	}

R dir.go => page.go +101 -123
@@ 16,35 16,29 @@ import (
	"gopkg.in/yaml.v3"
)

// Dir represents a directory.
type Dir struct {
	Permalink string
	Pages     []*Page
	Dirs      []*Dir
	index     *Page             // The index page.
	feeds     map[string][]byte // Atom/RSS/Custom feeds.
	path      string            // relative to the content dir
}

// Page represents a page.
type Page struct {
	Title     string
	Date      time.Time
	Weight    int
	Permalink string `yaml:"-"`
	FilePath  string `yaml:"-"`
	Content   string `yaml:"-"`
	Params    map[string]interface{}
	Prev      *Page `yaml:"-"`
	Next      *Page `yaml:"-"`
	FilePath  string  `yaml:"-"`
	Permalink string  `yaml:"-"`
	Content   string  `yaml:"-"`
	Prev      *Page   `yaml:"-"`
	Next      *Page   `yaml:"-"`
	Pages     []*Page `yaml:"-"`
	Dirs      []*Page `yaml:"-"`
	feeds     map[string][]byte
	index     bool
}

// read reads from a directory and indexes the files and directories within it.
func (d *Dir) read(srcDir string, task *Task, cfg *Site) error {
	return d._read(srcDir, "", task, cfg)
func (p *Page) read(srcDir string, task *Task, cfg *Site) error {
	return p._read(srcDir, "", task, cfg)
}

func (d *Dir) _read(srcDir, path string, task *Task, cfg *Site) error {
func (p *Page) _read(srcDir, path string, task *Task, cfg *Site) error {
	entries, err := ioutil.ReadDir(pathpkg.Join(srcDir, path))
	if err != nil {
		return err


@@ 58,11 52,11 @@ func (d *Dir) _read(srcDir, path string, task *Task, cfg *Site) error {
				continue
			}
			// Gather directory data
			dir := &Dir{Permalink: "/" + path + "/", path: path}
			dir := &Page{Permalink: "/" + path + "/", FilePath: path}
			if err := dir._read(srcDir, path, task, cfg); err != nil {
				return err
			}
			d.Dirs = append(d.Dirs, dir)
			p.Dirs = append(p.Dirs, dir)
		} else if ext := pathpkg.Ext(name); task.Match(ext) {
			// Ignore pages beginning with "_" with the exception of _index pages
			namePrefix := strings.TrimSuffix(name, ext)


@@ 76,7 70,13 @@ func (d *Dir) _read(srcDir, path string, task *Task, cfg *Site) error {
				return err
			}

			page := &Page{}
			page := &Page{
				FilePath: path,
			}
			if namePrefix == "_index" {
				p.index = true
				page = p
			}

			// Try to parse the date from the page filename
			const layout = "2006-01-02"


@@ 123,12 123,7 @@ func (d *Dir) _read(srcDir, path string, task *Task, cfg *Site) error {
			}
			page.Content = string(content)

			page.FilePath = path

			if namePrefix == "_index" {
				page.Permalink = d.Permalink
				d.index = page
			} else {
			if !page.index {
				if namePrefix == "index" {
					path = "/" + strings.TrimSuffix(path, name)
				} else {


@@ 140,12 135,12 @@ func (d *Dir) _read(srcDir, path string, task *Task, cfg *Site) error {
					}
				}
				page.Permalink = path
				if permalink, ok := cfg.permalinks[d.Permalink]; ok {
				if permalink, ok := cfg.permalinks[p.Permalink]; ok {
					var b strings.Builder
					permalink.Execute(&b, page)
					page.Permalink = b.String()
				}
				d.Pages = append(d.Pages, page)
				p.Pages = append(p.Pages, page)
			}
		}
	}


@@ 153,44 148,44 @@ func (d *Dir) _read(srcDir, path string, task *Task, cfg *Site) error {
}

// process processes the directory's contents.
func (d *Dir) process(cfg *Site, task *Task) error {
func (p *Page) process(cfg *Site, task *Task) error {
	// Build feeds before templates are applied to the page contents
	for _, feed := range task.feeds[d.path] {
		b, err := d.buildFeed(cfg, feed)
	for _, feed := range task.feeds[p.FilePath] {
		b, err := p.buildFeed(cfg, feed)
		if err != nil {
			return err
		}
		d.addFeed(feed.Output, b)
		p.addFeed(feed.Output, b)
	}

	if task.TemplateExt != "" {
		// Create index
		if d.index != nil {
			tmpl, ok := cfg.templates.FindTemplate(d.Permalink, "index"+task.TemplateExt)
		if p.index {
			tmpl, ok := cfg.templates.FindTemplate(p.Permalink, "index"+task.TemplateExt)
			if ok {
				var b strings.Builder
				if err := tmpl.Execute(&b, d); err != nil {
				if err := tmpl.Execute(&b, p); err != nil {
					return err
				}
				d.index.Content = b.String()
				p.Content = b.String()
			}
		}

		// Process pages
		for i := range d.Pages {
		for i := range p.Pages {
			var b strings.Builder
			tmpl, ok := cfg.templates.FindTemplate(d.Permalink, "page"+task.TemplateExt)
			tmpl, ok := cfg.templates.FindTemplate(p.Permalink, "page"+task.TemplateExt)
			if ok {
				if err := tmpl.Execute(&b, d.Pages[i]); err != nil {
				if err := tmpl.Execute(&b, p.Pages[i]); err != nil {
					return err
				}
				d.Pages[i].Content = b.String()
				p.Pages[i].Content = b.String()
			}
		}
	}

	// Process subdirectories
	for _, d := range d.Dirs {
	for _, d := range p.Dirs {
		if err := d.process(cfg, task); err != nil {
			return err
		}


@@ 199,7 194,7 @@ func (d *Dir) process(cfg *Site, task *Task) error {
}

// buildFeed build the feed of the directory
func (d Dir) buildFeed(cfg *Site, feed Feed) ([]byte, error) {
func (p *Page) buildFeed(cfg *Site, feed Feed) ([]byte, error) {
	// Feed represents a feed.
	type Feed struct {
		Title     string


@@ 207,7 202,7 @@ func (d Dir) buildFeed(cfg *Site, feed Feed) ([]byte, error) {
		Pages     []*Page
	}

	tmpl, ok := cfg.templates.FindTemplate(d.Permalink, feed.Template)
	tmpl, ok := cfg.templates.FindTemplate(p.Permalink, feed.Template)
	if !ok {
		return nil, fmt.Errorf("failed to generate feed %q: missing feed template %q", feed.Title, feed.Template)
	}


@@ 215,8 210,8 @@ func (d Dir) buildFeed(cfg *Site, feed Feed) ([]byte, error) {
	var b bytes.Buffer
	data := Feed{
		Title:     feed.Title,
		Permalink: d.Permalink,
		Pages:     d.Pages,
		Permalink: p.Permalink,
		Pages:     p.Pages,
	}
	if err := tmpl.Execute(&b, data); err != nil {
		return nil, err


@@ 224,48 219,37 @@ func (d Dir) buildFeed(cfg *Site, feed Feed) ([]byte, error) {
	return b.Bytes(), nil
}

func (d *Dir) addFeed(name string, content []byte) {
	if d.feeds == nil {
		d.feeds = map[string][]byte{}
func (p *Page) addFeed(name string, content []byte) {
	if p.feeds == nil {
		p.feeds = map[string][]byte{}
	}
	d.feeds[name] = content
	p.feeds[name] = content
}

// write writes the directory's contents to the provided destination path.
func (d *Dir) write(dstDir string, task *Task) error {
	dirPath := pathpkg.Join(dstDir, d.Permalink)
func (p *Page) write(dstDir string, task *Task) error {
	dirPath := pathpkg.Join(dstDir, p.Permalink)

	// Write pages
	pages := d.Pages
	if d.index != nil {
		pages = append(pages, d.index)
	}
	for _, page := range pages {
		path := page.Permalink
		if !task.UglyURLs || page == d.index {
			path = pathpkg.Join(path, "index"+task.OutputExt)
	for _, page := range p.Pages {
		dstPath := pathpkg.Join(dstDir, page.Permalink)
		if !task.UglyURLs {
			dstPath = pathpkg.Join(dstPath, "index"+task.OutputExt)
		}
		var content []byte
		if cmd := task.Postprocess; cmd != "" {
			var buf bytes.Buffer
			if err := execute(cmd, strings.NewReader(page.Content), &buf); err != nil {
				return err
			}
			content = buf.Bytes()
		} else {
			content = []byte(page.Content)
		if err := page.writeTo(dstPath, task); err != nil {
			return err
		}

		dstPath := pathpkg.Join(dstDir, path)
		dir := pathpkg.Dir(dstPath)
		os.MkdirAll(dir, 0755)
		if err := os.WriteFile(dstPath, content, 0644); err != nil {
	}
	// Write index page
	if p.index {
		dstPath := pathpkg.Join(dirPath, p.Permalink, "index"+task.OutputExt)
		if err := p.writeTo(dstPath, task); err != nil {
			return err
		}
	}

	// Write feeds
	for name, content := range d.feeds {
	for name, content := range p.feeds {
		dstPath := pathpkg.Join(dstDir, name)
		os.MkdirAll(dirPath, 0755)
		if err := os.WriteFile(dstPath, content, 0644); err != nil {


@@ 274,40 258,60 @@ func (d *Dir) write(dstDir string, task *Task) error {
	}

	// Write subdirectories
	for _, dir := range d.Dirs {
	for _, dir := range p.Dirs {
		dir.write(dstDir, task)
	}
	return nil
}

func (p *Page) writeTo(dstPath string, task *Task) error {
	var content []byte
	if cmd := task.Postprocess; cmd != "" {
		var buf bytes.Buffer
		if err := execute(cmd, strings.NewReader(p.Content), &buf); err != nil {
			return err
		}
		content = buf.Bytes()
	} else {
		content = []byte(p.Content)
	}

	dir := pathpkg.Dir(dstPath)
	os.MkdirAll(dir, 0755)
	if err := os.WriteFile(dstPath, content, 0644); err != nil {
		return err
	}
	return nil
}

// sort sorts the directory's pages by weight, then date, then filepath.
func (d *Dir) sort() {
	sort.Slice(d.Pages, func(i, j int) bool {
		pi, pj := d.Pages[i], d.Pages[j]
func (p *Page) sort() {
	sort.Slice(p.Pages, func(i, j int) bool {
		pi, pj := p.Pages[i], p.Pages[j]
		return pi.FilePath < pj.FilePath
	})

	sort.SliceStable(d.Pages, func(i, j int) bool {
		pi, pj := d.Pages[i], d.Pages[j]
	sort.SliceStable(p.Pages, func(i, j int) bool {
		pi, pj := p.Pages[i], p.Pages[j]
		return pi.Date.After(pj.Date)
	})

	sort.SliceStable(d.Pages, func(i, j int) bool {
		pi, pj := d.Pages[i], d.Pages[j]
	sort.SliceStable(p.Pages, func(i, j int) bool {
		pi, pj := p.Pages[i], p.Pages[j]
		return pi.Weight < pj.Weight
	})

	for i := range d.Pages {
	for i := range p.Pages {
		if i-1 >= 0 {
			d.Pages[i].Prev = d.Pages[i-1]
			p.Pages[i].Prev = p.Pages[i-1]
		}
		if i+1 < len(d.Pages) {
			d.Pages[i].Next = d.Pages[i+1]
		if i+1 < len(p.Pages) {
			p.Pages[i].Next = p.Pages[i+1]
		}
	}

	// Sort subdirectories
	for _, d := range d.Dirs {
	for _, d := range p.Dirs {
		d.sort()
	}
}


@@ 326,48 330,22 @@ func execute(command string, input io.Reader, output io.Writer) error {
	return nil
}

func (d *Dir) Title() string {
	return d.index.Title
}

func (d *Dir) Date() time.Time {
	return d.index.Date
}

func (d *Dir) Content() string {
	return d.index.Content
}

func (d *Dir) Params() map[string]interface{} {
	return d.index.Params
}

func (d *Dir) getDir(path string) *Dir {
func (p *Page) getPage(path string) *Page {
	// XXX: This is inefficient
	if d.Permalink == path {
		return d
	if p.Permalink == path {
		return p
	}
	for _, dir := range d.Dirs {
		if dir.Permalink == path {
			return dir
	for _, page := range p.Pages {
		if page.FilePath == path {
			return page
		}
	}
	for i := range d.Dirs {
		if dir := d.Dirs[i].getDir(path); dir != nil {
	for _, dir := range p.Dirs {
		if dir.Permalink == path {
			return dir
		}
	}
	return nil
}

func (d *Dir) getPage(path string) *Page {
	// XXX: This is inefficient
	for _, page := range d.Pages {
		if page.FilePath == path {
			return page
		}
	}
	for _, dir := range d.Dirs {
	for _, dir := range p.Dirs {
		if page := dir.getPage(path); page != nil {
			return page
		}

M site.go => site.go +1 -5
@@ 18,7 18,7 @@ type Site struct {
	Generated  time.Time         `toml:"-"`
	permalinks map[string]*template.Template
	templates  Templates
	root       *Dir
	root       *Page
}

// Task represents a site build task.


@@ 103,10 103,6 @@ func LoadSite(config string) (*Site, error) {
	return site, nil
}

func (s *Site) dir(path string) *Dir {
	return s.root.getDir(path)
}

func (s *Site) page(path string) *Page {
	return s.root.getPage(path)
}