Add dir and page functions and fix partial function

This commit is contained in:
adnano 2021-05-14 00:17:10 -04:00
parent 70cc182bf1
commit 0e54f9eec5
5 changed files with 114 additions and 94 deletions

53
dir.go
View file

@ -38,24 +38,12 @@ type Page struct {
Next *Page `yaml:"-"`
}
// NewDir returns a new Dir with the given path.
func NewDir(path string) *Dir {
if path == "" {
path = "/"
} else {
path = "/" + path + "/"
}
return &Dir{
Path: path,
}
}
// read reads from a directory and indexes the files and directories within it.
func (d *Dir) read(srcDir string, task *Task, cfg *Config) error {
func (d *Dir) read(srcDir string, task *Task, cfg *Site) error {
return d._read(srcDir, "", task, cfg)
}
func (d *Dir) _read(srcDir, path string, task *Task, cfg *Config) error {
func (d *Dir) _read(srcDir, path string, task *Task, cfg *Site) error {
entries, err := ioutil.ReadDir(pathpkg.Join(srcDir, path))
if err != nil {
return err
@ -69,7 +57,7 @@ func (d *Dir) _read(srcDir, path string, task *Task, cfg *Config) error {
continue
}
// Gather directory data
dir := NewDir(path)
dir := &Dir{Path: "/" + path + "/"}
if err := dir._read(srcDir, path, task, cfg); err != nil {
return err
}
@ -160,7 +148,7 @@ func (d *Dir) _read(srcDir, path string, task *Task, cfg *Config) error {
}
// process processes the directory's contents.
func (d *Dir) process(cfg *Config, task *Task) error {
func (d *Dir) process(cfg *Site, task *Task) error {
if task.TemplateExt != "" {
// Create index
if d.index != nil {
@ -321,3 +309,36 @@ func (d *Dir) Date() time.Time {
func (d *Dir) Content() string {
return d.index.Content
}
func (d *Dir) getDir(path string) *Dir {
// XXX: This is inefficient
if d.Path == path {
return d
}
for _, dir := range d.Dirs {
if dir.Path == path {
return dir
}
}
for i := range d.Dirs {
if dir := d.Dirs[i].getDir(path); dir != nil {
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 {
if page := dir.getPage(path); page != nil {
return page
}
}
return nil
}

View file

@ -7,15 +7,22 @@ import (
"strings"
)
var funcs = map[string]interface{}{
// funcs returns functions for use in templates.
func (s *Site) funcs() map[string]interface{} {
return map[string]interface{}{
"dir": s.dir,
"page": s.page,
"path": func() _path { return _path{} },
"partial": s.templates.ExecutePartial,
"reverse": reverse,
"strings": func() _strings { return _strings{} },
"safeCSS": func(s string) template.CSS { return template.CSS(s) },
"safeHTML": func(s string) template.HTML { return template.HTML(s) },
"safeHTMLAttr": func(s string) template.HTMLAttr { return template.HTMLAttr(s) },
"safeCSS": func(s string) template.CSS { return template.CSS(s) },
"safeJS": func(s string) template.JS { return template.JS(s) },
"safeURL": func(s string) template.URL { return template.URL(s) },
"site": func() *Site { return s },
"strings": func() _strings { return _strings{} },
}
}
type _path struct{}

33
main.go
View file

@ -49,30 +49,19 @@ func build() {
flags.StringVar(&config, "c", "config.toml", "the configuration file to use")
flags.Parse(os.Args[2:])
// Load config
cfg, err := LoadConfig(config)
site, err := LoadSite(config)
if err != nil {
log.Fatal(err)
}
templateExts := []string{}
for _, task := range cfg.Tasks {
if task.TemplateExt != "" {
templateExts = append(templateExts, task.TemplateExt)
}
}
if err := cfg.templates.Load("templates", templateExts); err != nil {
log.Fatal(err)
}
if err := run(cfg); err != nil {
if err := site.run(); err != nil {
log.Fatal(err)
}
}
func run(cfg *Config) error {
for _, task := range cfg.Tasks {
err := runTask(cfg, task)
func (site *Site) run() error {
for _, task := range site.Tasks {
err := site.runTask(task)
if err != nil {
return err
}
@ -80,19 +69,19 @@ func run(cfg *Config) error {
return nil
}
func runTask(cfg *Config, task *Task) error {
func (s *Site) runTask(task *Task) error {
// Read content
dir := NewDir("")
if err := dir.read("content", task, cfg); err != nil {
s.root = &Dir{Path: "/"}
if err := s.root.read("content", task, s); err != nil {
return err
}
dir.sort()
s.root.sort()
// Process content
if err := dir.process(cfg, task); err != nil {
if err := s.root.process(s, task); err != nil {
return err
}
// Write content
if err := dir.write(task.OutputDir, task); err != nil {
if err := s.root.write(task.OutputDir, task); err != nil {
return err
}
// Copy static files

View file

@ -3,21 +3,21 @@ package main
import (
"fmt"
"os"
"strings"
"text/template"
"github.com/pelletier/go-toml"
)
// Config contains site configuration.
type Config struct {
// Site represents a site.
type Site struct {
Title string `toml:"title"`
URLs []string `toml:"urls"`
Tasks []*Task `toml:"tasks"`
Feeds map[string]string `toml:"feeds"`
Permalinks map[string]string `toml:"permalinks"`
permalinks map[string]*template.Template
templates *Templates
templates Templates
root *Dir
}
// Task represents a site build task.
@ -41,57 +41,51 @@ func (t *Task) Match(ext string) bool {
return false
}
// LoadConfig loads the configuration from the provided path.
func LoadConfig(path string) (*Config, error) {
f, err := os.Open(path)
// LoadSite loads the site with the given configuration file.
func LoadSite(config string) (*Site, error) {
f, err := os.Open(config)
if err != nil {
return nil, err
}
defer f.Close()
c := &Config{}
if err := toml.NewDecoder(f).Decode(c); err != nil {
site := &Site{}
if err := toml.NewDecoder(f).Decode(site); err != nil {
return nil, err
}
funcs := site.funcs()
// Parse permalinks
c.permalinks = map[string]*template.Template{}
for s := range c.Permalinks {
t := template.New(fmt.Sprintf("permalink %q", s)).Funcs(funcs)
_, err := t.Parse(c.Permalinks[s])
site.permalinks = map[string]*template.Template{}
for path := range site.Permalinks {
t := template.New(fmt.Sprintf("permalink %q", path)).Funcs(funcs)
_, err := t.Parse(site.Permalinks[path])
if err != nil {
return nil, err
}
c.permalinks[s] = t
site.permalinks[path] = t
}
// Site contains site metadata passed to templates
type Site struct {
Title string
URLs []string
// Load templates
templateExts := []string{}
for _, task := range site.Tasks {
if task.TemplateExt != "" {
templateExts = append(templateExts, task.TemplateExt)
}
}
site.templates.Funcs(funcs)
if err := site.templates.Load("templates", templateExts); err != nil {
return nil, err
}
funcs["site"] = func() Site {
return Site{
Title: c.Title,
URLs: c.URLs,
}
}
funcs["partial"] = func(name string, data interface{}) (interface{}, error) {
t, ok := c.templates.FindPartial(name)
if !ok {
return "", fmt.Errorf("Error: partial %q not found", name)
}
var b strings.Builder
if err := t.Execute(&b, data); err != nil {
return "", err
}
return b.String(), nil
return site, nil
}
// Initialize templates
c.templates = NewTemplates()
c.templates.Funcs(funcs)
return c, 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)
}

View file

@ -1,6 +1,7 @@
package main
import (
"fmt"
htemplate "html/template"
"io"
"io/fs"
@ -60,14 +61,6 @@ type Templates struct {
funcs map[string]interface{}
}
// NewTemplates returns a new Templates with the default templates.
func NewTemplates() *Templates {
t := &Templates{
tmpls: map[string]Template{},
}
return t
}
// Funcs sets the functions available to newly created templates.
func (t *Templates) Funcs(funcs map[string]interface{}) {
t.funcs = funcs
@ -75,6 +68,9 @@ func (t *Templates) Funcs(funcs map[string]interface{}) {
// LoadTemplate loads a template from the provided filenames.
func (t *Templates) LoadTemplate(name string, filenames ...string) error {
if t.tmpls == nil {
t.tmpls = map[string]Template{}
}
if ext := pathpkg.Ext(name); ext == ".html" || ext == ".xml" {
return t.loadHTMLTemplate(name, filenames...)
}
@ -170,3 +166,16 @@ func (t *Templates) FindPartial(name string) (Template, bool) {
}
return nil, false
}
// ExecutePartial executes the partial with the given name.
func (t *Templates) ExecutePartial(name string, data interface{}) (string, error) {
tmpl, ok := t.FindPartial(name)
if !ok {
return "", fmt.Errorf("Error: partial %q not found", name)
}
var b strings.Builder
if err := tmpl.Execute(&b, data); err != nil {
return "", err
}
return b.String(), nil
}