begin implementing separate metadata parsing
This commit is contained in:
parent
709a2738f9
commit
35c14f09c0
6 changed files with 192 additions and 2 deletions
|
@ -24,6 +24,7 @@ type PageData struct {
|
|||
FooterName string
|
||||
Footer template.HTML
|
||||
Template string
|
||||
Type string
|
||||
}
|
||||
|
||||
type Metadata map[string]interface{}
|
||||
|
@ -49,6 +50,32 @@ func processWithYaml(f []byte) (Metadata, []byte, error) {
|
|||
return meta, []byte(split[2]), nil
|
||||
}
|
||||
|
||||
func processFrontmatter(p string) (Metadata, error) {
|
||||
// read only the first three lines
|
||||
f, err := util.ReadNLines(p, 3)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Check if the file has valid metadata
|
||||
trimmed := bytes.TrimSpace(f)
|
||||
normalized := strings.ReplaceAll(string(trimmed), "\r\n", "\n")
|
||||
if !strings.HasPrefix(normalized, ("---\n")) {
|
||||
// No valid yaml, so return the entire content
|
||||
return nil, nil
|
||||
}
|
||||
// Separate YAML from rest of document
|
||||
split := strings.SplitN(normalized, "---\n", 3)
|
||||
if len(split) < 3 {
|
||||
return nil, fmt.Errorf("Invalid frontmatter format.")
|
||||
}
|
||||
var meta Metadata
|
||||
// Parse YAML
|
||||
if err := yaml.Unmarshal([]byte(split[1]), &meta); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return meta, nil
|
||||
}
|
||||
|
||||
func buildPageData(m Metadata, in string, out string, settings *Settings) *PageData {
|
||||
p := &PageData{}
|
||||
if title, ok := m["title"].(string); ok {
|
||||
|
@ -92,8 +119,10 @@ func buildPageData(m Metadata, in string, out string, settings *Settings) *PageD
|
|||
// TODO: Don't hard code posts dir name
|
||||
if t, ok := m["type"].(string); util.InDir(in, "posts") && !ok || (ok && t == "article" || t == "post") {
|
||||
p.Template = (settings.ArticleTemplate)
|
||||
p.Type = "post"
|
||||
} else {
|
||||
p.Template = (settings.DefaultTemplate)
|
||||
p.Type = ""
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
|
37
internal/builder/build_page_test.go
Normal file
37
internal/builder/build_page_test.go
Normal file
|
@ -0,0 +1,37 @@
|
|||
package builder
|
||||
|
||||
import "testing"
|
||||
|
||||
func Test_processWithYaml(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string // description of this test case
|
||||
// Named input parameters for target function.
|
||||
f []byte
|
||||
want Metadata
|
||||
want2 []byte
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, got2, gotErr := processWithYaml(tt.f)
|
||||
if gotErr != nil {
|
||||
if !tt.wantErr {
|
||||
t.Errorf("processWithYaml() failed: %v", gotErr)
|
||||
}
|
||||
return
|
||||
}
|
||||
if tt.wantErr {
|
||||
t.Fatal("processWithYaml() succeeded unexpectedly")
|
||||
}
|
||||
// TODO: update the condition below to compare got with tt.want.
|
||||
if true {
|
||||
t.Errorf("processWithYaml() = %v, want %v", got, tt.want)
|
||||
}
|
||||
if true {
|
||||
t.Errorf("processWithYaml() = %v, want %v", got2, tt.want2)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
73
internal/builder/process.go
Normal file
73
internal/builder/process.go
Normal file
|
@ -0,0 +1,73 @@
|
|||
package builder
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/ficcdaf/zona/internal/util"
|
||||
)
|
||||
|
||||
type ProcessMemory struct {
|
||||
// Pages holds all page data that may be
|
||||
// needed while building *other* pages.
|
||||
Pages []*Page
|
||||
// Queue is a FIFO queue of Pages indexes to be built.
|
||||
// queue should be constructed after all the Pages have been parsed
|
||||
Queue []int
|
||||
// Posts is an array of pointers to post pages
|
||||
Posts []*Page
|
||||
}
|
||||
|
||||
type Page struct {
|
||||
Data *PageData
|
||||
Ext string
|
||||
InPath string
|
||||
OutPath string
|
||||
Copy bool
|
||||
}
|
||||
|
||||
// processFile processes the metadata only
|
||||
// of each file
|
||||
func processFile(inPath string, entry fs.DirEntry, err error, outRoot string, settings *Settings, pm *ProcessMemory) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var toProcess bool
|
||||
var outPath string
|
||||
var ext string
|
||||
if !entry.IsDir() {
|
||||
ext = filepath.Ext(inPath)
|
||||
outPath = util.ReplaceRoot(inPath, outRoot)
|
||||
switch ext {
|
||||
case ".md":
|
||||
// fmt.Println("Processing markdown...")
|
||||
toProcess = true
|
||||
outPath = util.ChangeExtension(outPath, ".html")
|
||||
// If it's not a file we need to process,
|
||||
// we simply copy it to the destination path.
|
||||
default:
|
||||
toProcess = false
|
||||
}
|
||||
}
|
||||
page := &Page{
|
||||
nil,
|
||||
ext,
|
||||
inPath,
|
||||
outPath,
|
||||
!toProcess,
|
||||
}
|
||||
if toProcess {
|
||||
// process its frontmatter here
|
||||
m, err := processFrontmatter(inPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pd := buildPageData(m, inPath, outPath, settings)
|
||||
if pd.Type == "post" {
|
||||
pm.Posts = append(pm.Posts, page)
|
||||
}
|
||||
page.Data = pd
|
||||
}
|
||||
pm.Pages = append(pm.Pages, page)
|
||||
return nil
|
||||
}
|
|
@ -8,7 +8,8 @@ import (
|
|||
"github.com/ficcdaf/zona/internal/util"
|
||||
)
|
||||
|
||||
func processFile(inPath string, entry fs.DirEntry, err error, outRoot string, settings *Settings) error {
|
||||
// TODO: Process the metadata and build a queue of files to convert here instead of converting them immediately
|
||||
func buildFile(inPath string, entry fs.DirEntry, err error, outRoot string, settings *Settings) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -46,7 +47,7 @@ func processFile(inPath string, entry fs.DirEntry, err error, outRoot string, se
|
|||
|
||||
func Traverse(root string, outRoot string, settings *Settings) error {
|
||||
walkFunc := func(path string, entry fs.DirEntry, err error) error {
|
||||
return processFile(path, entry, err, outRoot, settings)
|
||||
return buildFile(path, entry, err, outRoot, settings)
|
||||
}
|
||||
err := filepath.WalkDir(root, walkFunc)
|
||||
return err
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
@ -56,3 +58,25 @@ func CopyFile(inPath string, outPath string) error {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ReadNLines reads the first N lines from a file as a single byte array
|
||||
func ReadNLines(filename string, n int) ([]byte, error) {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var buffer bytes.Buffer
|
||||
scanner := bufio.NewScanner(file)
|
||||
for i := 0; i < 3 && scanner.Scan(); i++ {
|
||||
buffer.Write(scanner.Bytes())
|
||||
buffer.WriteByte('\n')
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
|
26
internal/util/queue.go
Normal file
26
internal/util/queue.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
package util
|
||||
|
||||
// Enqueue appends an int to the queue
|
||||
func Enqueue(queue []int, element int) []int {
|
||||
queue = append(queue, element)
|
||||
return queue
|
||||
}
|
||||
|
||||
// Dequeue pops the first element of the queue
|
||||
func Dequeue(queue []int) (int, []int) {
|
||||
element := queue[0] // The first element is the one to be dequeued.
|
||||
if len(queue) == 1 {
|
||||
tmp := []int{}
|
||||
return element, tmp
|
||||
}
|
||||
return element, queue[1:] // Slice off the element once it is dequeued.
|
||||
}
|
||||
|
||||
func Tail(queue []int) int {
|
||||
l := len(queue)
|
||||
if l == 0 {
|
||||
return -1
|
||||
} else {
|
||||
return l - 1
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue