mirror of
https://git.sr.ht/~adnano/kiln
synced 2024-10-30 01:13:08 +00:00
Refactor
This commit is contained in:
parent
81bf4f8e9b
commit
d0713476c0
80
index.go
80
index.go
|
@ -1,80 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Index struct {
|
|
||||||
Files map[string][]byte
|
|
||||||
Dirs map[string][]string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewIndex() *Index {
|
|
||||||
return &Index{
|
|
||||||
Files: map[string][]byte{},
|
|
||||||
Dirs: map[string][]string{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadDir reads a directory and indexes the files and directories within it.
|
|
||||||
func (index *Index) ReadDir(srcDir string, dstDir string) error {
|
|
||||||
entries, err := ioutil.ReadDir(srcDir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, entry := range entries {
|
|
||||||
name := entry.Name()
|
|
||||||
srcPath := filepath.Join(srcDir, name)
|
|
||||||
dstPath := filepath.Join(dstDir, name)
|
|
||||||
|
|
||||||
if entry.IsDir() {
|
|
||||||
index.Dirs[dstPath] = []string{}
|
|
||||||
index.ReadDir(srcPath, dstPath)
|
|
||||||
} else {
|
|
||||||
content, err := ioutil.ReadFile(srcPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
index.Files[dstPath] = content
|
|
||||||
index.Dirs[dstDir] = append(index.Dirs[dstDir], dstPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write writes the contents of the Index to the provided destination directory.
|
|
||||||
func (index *Index) Write(dstDir string) error {
|
|
||||||
// Empty the destination directory
|
|
||||||
if err := os.RemoveAll(dstDir); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := os.MkdirAll(dstDir, 0755); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for path := range index.Files {
|
|
||||||
// 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 := index.Files[path]
|
|
||||||
if _, err := f.Write(data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
212
kiln.go
Normal file
212
kiln.go
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Site struct {
|
||||||
|
Files map[string][]byte
|
||||||
|
Dirs map[string][]string
|
||||||
|
Pages map[string]*Page
|
||||||
|
Templates *template.Template
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadSite(srcDir string) (*Site, error) {
|
||||||
|
tmpl, err := template.New("templates").ParseGlob("templates/*.gmi")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
site := &Site{
|
||||||
|
Files: map[string][]byte{},
|
||||||
|
Dirs: map[string][]string{},
|
||||||
|
Pages: map[string]*Page{},
|
||||||
|
Templates: tmpl,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := site.ReadDir(srcDir, ""); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return site, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadDir reads a directory and indexes the files and directories within it.
|
||||||
|
func (s *Site) ReadDir(srcDir string, dstDir string) error {
|
||||||
|
entries, err := ioutil.ReadDir(srcDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
name := entry.Name()
|
||||||
|
srcPath := filepath.Join(srcDir, name)
|
||||||
|
dstPath := filepath.Join(dstDir, name)
|
||||||
|
|
||||||
|
if entry.IsDir() {
|
||||||
|
s.Dirs[dstPath] = []string{}
|
||||||
|
s.ReadDir(srcPath, dstPath)
|
||||||
|
} else {
|
||||||
|
content, err := ioutil.ReadFile(srcPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Files[dstPath] = content
|
||||||
|
s.Dirs[dstDir] = append(s.Dirs[dstDir], dstPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes the contents of the Index to the provided destination directory.
|
||||||
|
func (s *Site) Write(dstDir string) error {
|
||||||
|
// Empty the destination directory
|
||||||
|
if err := os.RemoveAll(dstDir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(dstDir, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for path := range s.Files {
|
||||||
|
// 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.Files[path]
|
||||||
|
if _, err := f.Write(data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manipulate processes and manipulates the site's content.
|
||||||
|
func (s *Site) Manipulate() error {
|
||||||
|
for path := range s.Files {
|
||||||
|
switch filepath.Ext(path) {
|
||||||
|
case ".gmi":
|
||||||
|
// Gather page data
|
||||||
|
content := string(s.Files[path])
|
||||||
|
page := NewPage(path, content)
|
||||||
|
|
||||||
|
builder := &strings.Builder{}
|
||||||
|
tmpl := s.Templates.Lookup("page.gmi")
|
||||||
|
if err := tmpl.Execute(builder, page); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
page.Content = builder.String()
|
||||||
|
s.Pages[path] = page
|
||||||
|
s.Files[path] = []byte(page.Content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for dir := range s.Dirs {
|
||||||
|
// Gather section data
|
||||||
|
section := NewSection(dir)
|
||||||
|
for _, path := range s.Dirs[dir] {
|
||||||
|
switch filepath.Ext(path) {
|
||||||
|
case ".gmi":
|
||||||
|
section.Pages = append(section.Pages, s.Pages[path])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dstPath := filepath.Join(dir, "index.gmi")
|
||||||
|
builder := &strings.Builder{}
|
||||||
|
tmpl := s.Templates.Lookup("section.gmi")
|
||||||
|
if err := tmpl.Execute(builder, section); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.Files[dstPath] = []byte(builder.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Page represents a page.
|
||||||
|
type Page struct {
|
||||||
|
// The path to this page.
|
||||||
|
Path string
|
||||||
|
// The permalink to this page.
|
||||||
|
Permalink string
|
||||||
|
// The title of this page, parsed from the Gemini contents.
|
||||||
|
Title string
|
||||||
|
// The date of the page. Dates are specified in the filename.
|
||||||
|
// Ex: 2020-09-22-hello-world.gmi
|
||||||
|
Date time.Time
|
||||||
|
// The content of this page.
|
||||||
|
Content string
|
||||||
|
}
|
||||||
|
|
||||||
|
var titleRE = regexp.MustCompile("^# ?([^#\r\n]+)")
|
||||||
|
|
||||||
|
func NewPage(path string, content string) *Page {
|
||||||
|
page := &Page{
|
||||||
|
Path: path,
|
||||||
|
Permalink: "/" + path,
|
||||||
|
Content: content,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to parse the date from the page filename
|
||||||
|
const layout = "2006-01-02"
|
||||||
|
base := filepath.Base(path)
|
||||||
|
if len(base) >= len(layout) {
|
||||||
|
dateStr := base[:len(layout)]
|
||||||
|
if date, err := time.Parse(layout, dateStr); err == nil {
|
||||||
|
page.Date = date
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to parse the title from the contents
|
||||||
|
if submatches := titleRE.FindStringSubmatch(content); submatches != nil {
|
||||||
|
page.Title = submatches[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return page
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section represents a section (i.e., a directory).
|
||||||
|
type Section struct {
|
||||||
|
// The path to this section.
|
||||||
|
Path string
|
||||||
|
// The permalink to this section.
|
||||||
|
Permalink string
|
||||||
|
// The pages in this section.
|
||||||
|
Pages []*Page
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSection returns a new Section with the given path.
|
||||||
|
func NewSection(path string) *Section {
|
||||||
|
var permalink string
|
||||||
|
if path == "" {
|
||||||
|
permalink = "/"
|
||||||
|
} else {
|
||||||
|
permalink = "/" + path + "/"
|
||||||
|
}
|
||||||
|
return &Section{
|
||||||
|
Path: path,
|
||||||
|
Permalink: permalink,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort sorts pages by date.
|
||||||
|
func (s *Section) Sort() {
|
||||||
|
// TODO: Implement this
|
||||||
|
}
|
85
main.go
85
main.go
|
@ -2,9 +2,6 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -14,91 +11,15 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func run() error {
|
func run() error {
|
||||||
index := NewIndex()
|
site, err := LoadSite("src")
|
||||||
if err := index.ReadDir("src", ""); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
manipulator, err := NewManipulator()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := manipulator.Manipulate(index); err != nil {
|
if err := site.Manipulate(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := site.Write("dst"); err != nil {
|
||||||
if err := index.Write("dst"); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Page represents a page.
|
|
||||||
type Page struct {
|
|
||||||
// The path to this page.
|
|
||||||
Path string
|
|
||||||
// The permalink to this page.
|
|
||||||
Permalink string
|
|
||||||
// The title of this page, parsed from the Gemini contents.
|
|
||||||
Title string
|
|
||||||
// The date of the page. Dates are specified in the filename.
|
|
||||||
// Ex: 2020-09-22-hello-world.gmi
|
|
||||||
Date time.Time
|
|
||||||
// The content of this page.
|
|
||||||
Content string
|
|
||||||
}
|
|
||||||
|
|
||||||
var titleRE = regexp.MustCompile("^# ?([^#\r\n]+)")
|
|
||||||
|
|
||||||
func NewPage(path string, content string) *Page {
|
|
||||||
page := &Page{
|
|
||||||
Path: path,
|
|
||||||
Permalink: "/" + path,
|
|
||||||
Content: content,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to parse the date from the page filename
|
|
||||||
const layout = "2006-01-02"
|
|
||||||
base := filepath.Base(path)
|
|
||||||
if len(base) >= len(layout) {
|
|
||||||
dateStr := base[:len(layout)]
|
|
||||||
if date, err := time.Parse(layout, dateStr); err == nil {
|
|
||||||
page.Date = date
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to parse the title from the contents
|
|
||||||
if submatches := titleRE.FindStringSubmatch(content); submatches != nil {
|
|
||||||
page.Title = submatches[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
return page
|
|
||||||
}
|
|
||||||
|
|
||||||
// Section represents a section (i.e., a directory).
|
|
||||||
type Section struct {
|
|
||||||
// The path to this section.
|
|
||||||
Path string
|
|
||||||
// The permalink to this section.
|
|
||||||
Permalink string
|
|
||||||
// The pages in this section.
|
|
||||||
Pages []*Page
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSection returns a new Section with the given path.
|
|
||||||
func NewSection(path string) *Section {
|
|
||||||
var permalink string
|
|
||||||
if path == "" {
|
|
||||||
permalink = "/"
|
|
||||||
} else {
|
|
||||||
permalink = "/" + path + "/"
|
|
||||||
}
|
|
||||||
return &Section{
|
|
||||||
Path: path,
|
|
||||||
Permalink: permalink,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort sorts pages by date.
|
|
||||||
func (s *Section) Sort() {
|
|
||||||
// TODO: Implement this
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,66 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"text/template"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Manipulator struct {
|
|
||||||
Pages map[string]*Page
|
|
||||||
|
|
||||||
// Templates
|
|
||||||
Templates *template.Template
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewManipulator() (*Manipulator, error) {
|
|
||||||
tmpl, err := template.New("templates").ParseGlob("templates/*.gmi")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &Manipulator{
|
|
||||||
Pages: map[string]*Page{},
|
|
||||||
Templates: tmpl,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Manipulator) Manipulate(index *Index) error {
|
|
||||||
for path := range index.Files {
|
|
||||||
switch filepath.Ext(path) {
|
|
||||||
case ".gmi":
|
|
||||||
// Gather page data
|
|
||||||
content := string(index.Files[path])
|
|
||||||
page := NewPage(path, content)
|
|
||||||
|
|
||||||
builder := &strings.Builder{}
|
|
||||||
tmpl := m.Templates.Lookup("page.gmi")
|
|
||||||
if err := tmpl.Execute(builder, page); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
page.Content = builder.String()
|
|
||||||
m.Pages[path] = page
|
|
||||||
index.Files[path] = []byte(page.Content)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for dir := range index.Dirs {
|
|
||||||
// Load section data
|
|
||||||
section := NewSection(dir)
|
|
||||||
for _, path := range index.Dirs[dir] {
|
|
||||||
switch filepath.Ext(path) {
|
|
||||||
case ".gmi":
|
|
||||||
section.Pages = append(section.Pages, m.Pages[path])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dstPath := filepath.Join(dir, "index.gmi")
|
|
||||||
builder := &strings.Builder{}
|
|
||||||
tmpl := m.Templates.Lookup("section.gmi")
|
|
||||||
if err := tmpl.Execute(builder, section); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
index.Files[dstPath] = []byte(builder.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
Loading…
Reference in a new issue