Refactor FrontMatter parsing to better fit hugo requirements

This commit is contained in:
Niklas Fasching 2018-12-19 18:14:27 +01:00
parent 724cf6c23e
commit 2295594970
2 changed files with 38 additions and 26 deletions

View file

@ -30,6 +30,7 @@ type Writer interface {
} }
type Node interface{} type Node interface{}
type FrontMatter = map[string]interface{}
type lexFn = func(line string) (t token, ok bool) type lexFn = func(line string) (t token, ok bool)
type parseFn = func(*Document, int, stopFn) (int, Node) type parseFn = func(*Document, int, stopFn) (int, Node)
@ -57,13 +58,14 @@ var lexFns = []lexFn{
var nilToken = token{"nil", -1, "", nil} var nilToken = token{"nil", -1, "", nil}
var DefaultFrontMatterHandler = func(k, v string) interface{} { func FrontMatterHandler(fm FrontMatter, k, v string) error {
switch k { switch k := strings.ToLower(k); k {
case "TAGS", "CATEGORIES", "ALIASES": case "tags", "categories", "aliases":
return strings.Fields(v) fm[k] = strings.Fields(v)
default: default:
return v fm[k] = v
} }
return nil
} }
func NewDocument() *Document { func NewDocument() *Document {
@ -118,9 +120,9 @@ func (d *Document) SetPath(path string) *Document {
return d return d
} }
func (d *Document) FrontMatter(input io.Reader, f func(string, string) interface{}) (_ map[string]interface{}, err error) { func GetFrontMatter(input io.Reader, f func(FrontMatter, string, string) error) (_ FrontMatter, err error) {
d := NewDocument()
defer func() { defer func() {
d.tokens = nil
if recovered := recover(); recovered != nil { if recovered := recover(); recovered != nil {
err = fmt.Errorf("could not parse input: %s", recovered) err = fmt.Errorf("could not parse input: %s", recovered)
} }
@ -133,9 +135,12 @@ func (d *Document) FrontMatter(input io.Reader, f func(string, string) interface
t := d.tokens[i] t := d.tokens[i]
return t.kind != "keyword" && !(t.kind == "text" && t.content == "") return t.kind != "keyword" && !(t.kind == "text" && t.content == "")
}) })
frontMatter := make(map[string]interface{}, len(d.BufferSettings)) frontMatter := make(FrontMatter, len(d.BufferSettings))
for k, v := range d.BufferSettings { for k, v := range d.BufferSettings {
frontMatter[k] = f(k, v) err := f(frontMatter, k, v)
if err != nil {
return nil, err
}
} }
return frontMatter, err return frontMatter, err
} }

View file

@ -9,18 +9,18 @@ import (
type frontMatterTest struct { type frontMatterTest struct {
name string name string
input string input string
handler func(string, string) interface{} handler func(FrontMatter, string, string) error
expected map[string]interface{} expected map[string]interface{}
} }
var frontMatterTests = []frontMatterTest{ var frontMatterTests = []frontMatterTest{
{`basic`, {`basic`,
`#+TITLE: The Title`, `#+TITLE: The Title`,
DefaultFrontMatterHandler, FrontMatterHandler,
map[string]interface{}{"TITLE": "The Title"}}, map[string]interface{}{"title": "The Title"}},
{`empty`, {`empty`,
`* No frontmatter here`, `* No frontmatter here`,
DefaultFrontMatterHandler, FrontMatterHandler,
map[string]interface{}{}}, map[string]interface{}{}},
{`custom handler`, {`custom handler`,
` `
@ -29,17 +29,18 @@ var frontMatterTests = []frontMatterTest{
#+TAGS: foo bar #+TAGS: foo bar
`, `,
func(k, v string) interface{} { func(fm FrontMatter, k, v string) error {
switch k { switch k := strings.ToLower(k); k {
case "TITLE": case "title":
return "Thanks For All The Fish" fm[k] = "Thanks For All The Fish"
return nil
default: default:
return DefaultFrontMatterHandler(k, v) return FrontMatterHandler(fm, k, v)
} }
}, },
map[string]interface{}{ map[string]interface{}{
"TITLE": "Thanks For All The Fish", "title": "Thanks For All The Fish",
"TAGS": []string{"foo", "bar"}, "tags": []string{"foo", "bar"},
}}, }},
{`multiple + ignored keyword`, {`multiple + ignored keyword`,
` `
@ -49,22 +50,28 @@ var frontMatterTests = []frontMatterTest{
#+OTHER: some other keyword #+OTHER: some other keyword
#+TAGS: this will become []string #+TAGS: this will become []string
#+ALIASES: foo bar
#+ALIASES: baz bam
#+categories: foo bar
something that's not a keyword or a text line without content something that's not a keyword or a text line without content
#+SUBTITLE: The Subtitle`, #+SUBTITLE: The Subtitle`,
DefaultFrontMatterHandler, FrontMatterHandler,
map[string]interface{}{ map[string]interface{}{
"TITLE": "The Title", "title": "The Title",
"AUTHOR": "The Author", "author": "The Author",
"OTHER": "some other keyword", "other": "some other keyword",
"TAGS": []string{"this", "will", "become", "[]string"}, "tags": []string{"this", "will", "become", "[]string"},
"aliases": []string{"foo", "bar", "baz", "bam"},
"categories": []string{"foo", "bar"},
}, },
}, },
} }
func TestParseFrontMatter(t *testing.T) { func TestParseFrontMatter(t *testing.T) {
for _, test := range frontMatterTests { for _, test := range frontMatterTests {
actual, err := NewDocument().FrontMatter(strings.NewReader(test.input), test.handler) actual, err := GetFrontMatter(strings.NewReader(test.input), test.handler)
if err != nil { if err != nil {
t.Errorf("%s\n got error: %s", test.name, err) t.Errorf("%s\n got error: %s", test.name, err)
continue continue