diff --git a/kiln.go b/kiln.go index 0368e2a..3044962 100644 --- a/kiln.go +++ b/kiln.go @@ -18,30 +18,25 @@ import ( // Site represents a kiln site. type Site struct { - Static map[string][]byte // Static files + URL string // Site URL Directory *Directory // Site directory Templates *template.Template // Templates } -// LoadSite loads and returns a Site. -// It reads site content from the specified source directory. -func LoadSite(srcDir string) (*Site, error) { +// Load loads the Site from the specified source directory. +func (s *Site) Load(srcDir string) error { + s.Directory = NewDirectory("") + tmpl, err := template.New("templates").ParseGlob("templates/*.gmi") if err != nil { - return nil, err + return err } + s.Templates = tmpl - site := &Site{ - Static: map[string][]byte{}, - Directory: NewDirectory(""), - Templates: tmpl, + if err := site.Directory.Read(""); err != nil { + return err } - - if err := site.Directory.Read(site, srcDir, ""); err != nil { - return nil, err - } - - return site, nil + return nil } // Write writes the contents of the Index to the provided destination directory. @@ -55,28 +50,6 @@ func (s *Site) Write(dstDir string, format OutputFormat) error { return err } - // Write the static files - for path := range s.Static { - // Create any parent directories - if dir := filepath.Dir(path); dir != "." { - dstPath := filepath.Join(dstDir, dir) - if err := os.MkdirAll(dstPath, 0755); err != nil { - return err - } - } - - // Write the file - dstPath := filepath.Join(dstDir, path) - f, err := os.Create(dstPath) - if err != nil { - return err - } - data := s.Static[path] - if _, err := f.Write(data); err != nil { - return err - } - } - // Write the directory return s.Directory.Write(dstDir, format) } @@ -88,27 +61,31 @@ func (s *Site) Manipulate(dir *Directory) error { path := filepath.Join(dir.Permalink, "index.gmi") var b bytes.Buffer tmpl := s.Templates.Lookup("index.gmi") - if tmpl != nil { - if err := tmpl.Execute(&b, dir); err != nil { - return err - } - content := b.Bytes() - permalink := filepath.Dir(path) - if permalink == "." { - permalink = "" - } - page := &Page{ - Permalink: "/" + permalink, - content: content, - } - dir.Index = page + if tmpl == nil { + tmpl = indexTmpl } + if err := tmpl.Execute(&b, dir); err != nil { + return err + } + content := b.Bytes() + permalink := filepath.Dir(path) + if permalink == "." { + permalink = "" + } + page := &Page{ + Permalink: "/" + permalink, + content: content, + } + dir.Index = page } // Manipulate pages for i := range dir.Pages { var b bytes.Buffer tmpl := s.Templates.Lookup("page.gmi") + if tmpl == nil { + tmpl = pageTmpl + } if err := tmpl.Execute(&b, dir.Pages[i]); err != nil { return err } @@ -123,6 +100,37 @@ func (s *Site) Sort() { s.Directory.Sort() } +type Feed struct { + Title string + Path string + SiteURL string + Updated time.Time + Entries []*Page +} + +// CreateFeeds creates Atom feeds. +func (s *Site) CreateFeeds() error { + const atomPath = "atom.xml" + var b bytes.Buffer + tmpl := s.Templates.Lookup(atomPath) + if tmpl == nil { + tmpl = atomTmpl + } + feed := &Feed{ + Title: "Example Feed", + Path: filepath.Join(s.Directory.Permalink, atomPath), + SiteURL: s.URL, + Updated: time.Now(), + Entries: s.Directory.Pages, + } + if err := tmpl.Execute(&b, feed); err != nil { + return err + } + path := filepath.Join(s.Directory.Permalink, atomPath) + s.Directory.Static[path] = b.Bytes() + return nil +} + // Page represents a page. type Page struct { // The permalink to this page. @@ -198,6 +206,8 @@ type Directory struct { Directories []*Directory // The index file (index.gmi). Index *Page + // Static files + Static map[string][]byte } // NewDirectory returns a new Directory with the given path. @@ -210,27 +220,29 @@ func NewDirectory(path string) *Directory { } return &Directory{ Permalink: permalink, + Static: map[string][]byte{}, } } // Read reads from a directory and indexes the files and directories within it. -func (d *Directory) Read(site *Site, srcDir string, path string) error { - entries, err := ioutil.ReadDir(srcDir) +func (d *Directory) Read(path string) error { + entries, err := ioutil.ReadDir(filepath.Join("src", path)) if err != nil { return err } for _, entry := range entries { name := entry.Name() - path := filepath.Join(path, name) - srcPath := filepath.Join(srcDir, path) - if entry.IsDir() { // Gather directory data + path := filepath.Join(path, name) dir := NewDirectory(path) - dir.Read(site, srcPath, path) + if err := dir.Read(path); err != nil { + return err + } d.Directories = append(d.Directories, dir) } else { + srcPath := filepath.Join("src", path, name) content, err := ioutil.ReadFile(srcPath) if err != nil { return err @@ -238,6 +250,7 @@ func (d *Directory) Read(site *Site, srcDir string, path string) error { switch filepath.Ext(name) { case ".gmi", ".gemini": + path := filepath.Join(path, name) // Gather page data page := NewPage(path, content) @@ -249,7 +262,8 @@ func (d *Directory) Read(site *Site, srcDir string, path string) error { default: // Static file - site.Static[path] = content + path := filepath.Join(path, name) + d.Static[path] = content } } } @@ -264,6 +278,19 @@ func (d *Directory) Write(dstDir string, format OutputFormat) error { return err } + // Write static files + for path := range d.Static { + dstPath := filepath.Join(dstDir, path) + f, err := os.Create(dstPath) + if err != nil { + return err + } + data := d.Static[path] + if _, err := f.Write(data); err != nil { + return err + } + } + // Write the files for _, page := range d.Pages { path, content := format(page) diff --git a/main.go b/main.go index 228edad..3a91d4a 100644 --- a/main.go +++ b/main.go @@ -11,11 +11,15 @@ import ( var ( serveSite bool toHtml bool + toAtom bool + site Site ) func init() { flag.BoolVar(&serveSite, "serve", false, "serve the site") flag.BoolVar(&toHtml, "html", false, "output HTML") + flag.BoolVar(&toAtom, "atom", false, "output Atom feed") + flag.StringVar(&site.URL, "url", "", "site URL") } func main() { @@ -31,14 +35,18 @@ func main() { // build the site func build() error { - site, err := LoadSite("src") - if err != nil { + if err := site.Load("src"); err != nil { return err } site.Sort() if err := site.Manipulate(site.Directory); err != nil { return err } + if toAtom { + if err := site.CreateFeeds(); err != nil { + return err + } + } if err := site.Write("dst", OutputGemini); err != nil { return err } diff --git a/templates.go b/templates.go new file mode 100644 index 0000000..d5eb68b --- /dev/null +++ b/templates.go @@ -0,0 +1,41 @@ +package main + +import "text/template" + +// Default templates + +const atom_xml = ` + +{{ .Title }} + + +{{ .Updated }} +{{ .SiteURL }}{{ .Path }} +{{ $siteURL := .SiteURL }} +{{ range .Entries }} + + {{ .Title }} + + {{ $siteURL }}{{ .Permalink }} + {{ .Date.Format "2006-01-02T15:04:05Z07:00" }} + + +{{ end }} +` + +const index_gmi = `# Index of {{ .Permalink }} + +{{ range .Directories }}=> {{ .Permalink }} +{{ end }} +{{ range .Pages }}=> {{ .Permalink }} +{{ end }}` + +const page_gmi = `# {{ .Title }} + +{{ .Content }}` + +var ( + atomTmpl = template.Must(template.New("atom.xml").Parse(atom_xml)) + indexTmpl = template.Must(template.New("index.gmi").Parse(index_gmi)) + pageTmpl = template.Must(template.New("page.gmi").Parse(page_gmi)) +)