mirror of
https://git.sr.ht/~adnano/kiln
synced 2024-12-29 06:43:45 +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 (
|
||||
"log"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -14,91 +11,15 @@ func main() {
|
|||
}
|
||||
|
||||
func run() error {
|
||||
index := NewIndex()
|
||||
if err := index.ReadDir("src", ""); err != nil {
|
||||
return err
|
||||
}
|
||||
manipulator, err := NewManipulator()
|
||||
site, err := LoadSite("src")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := manipulator.Manipulate(index); err != nil {
|
||||
if err := site.Manipulate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := index.Write("dst"); err != nil {
|
||||
if err := site.Write("dst"); err != nil {
|
||||
return err
|
||||
}
|
||||
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