Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/gohugoio/hugo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCameron Moore <moorereason@gmail.com>2016-12-27 00:23:20 +0300
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2017-03-11 19:52:25 +0300
commitf039e3be9e4a11808508c8cd3043b340deea040f (patch)
treedf3e709fc6321fd2a3e1d23efa2c3512d8ed4a94 /parser/frontmatter.go
parentddc8cc0082965143a650052a9aa538bac9133481 (diff)
parser: Refactor frontmatter parser and add tests
Lots of cleanups here: - Refactor InterfaceToConfig and InterfaceToFrontMatter to use io.Writer. - Simplify InterfaceToFrontMatter by wrapping InterfaceToConfig. - Export FrontmatterType since we return it in DetectFrontMatter. - Refactor removeTOMLIdentifier to avoid blindly replacing "+++". - Update HandleJSONMetaData to return an empty map on nil input. - Updates vendored goorgeous package and test for org-mode frontmatter. - Add tests and godoc comments. Coverage for parser package increased from 45.2% to 85.2%.
Diffstat (limited to 'parser/frontmatter.go')
-rw-r--r--parser/frontmatter.go161
1 files changed, 98 insertions, 63 deletions
diff --git a/parser/frontmatter.go b/parser/frontmatter.go
index e57a593ab..797c6fcf0 100644
--- a/parser/frontmatter.go
+++ b/parser/frontmatter.go
@@ -17,6 +17,7 @@ import (
"bytes"
"encoding/json"
"errors"
+ "io"
"strings"
"github.com/chaseadamsio/goorgeous"
@@ -25,113 +26,116 @@ import (
"gopkg.in/yaml.v2"
)
-type frontmatterType struct {
- markstart, markend []byte
- Parse func([]byte) (interface{}, error)
- includeMark bool
+// FrontmatterType represents a type of frontmatter.
+type FrontmatterType struct {
+ // Parse decodes content into a Go interface.
+ Parse func([]byte) (interface{}, error)
+
+ markstart, markend []byte // starting and ending delimiters
+ includeMark bool // include start and end mark in output
}
-func InterfaceToConfig(in interface{}, mark rune) ([]byte, error) {
+// InterfaceToConfig encodes a given input based upon the mark and writes to w.
+func InterfaceToConfig(in interface{}, mark rune, w io.Writer) error {
if in == nil {
- return []byte{}, errors.New("input was nil")
+ return errors.New("input was nil")
}
- b := new(bytes.Buffer)
-
switch mark {
case rune(YAMLLead[0]):
- by, err := yaml.Marshal(in)
- if err != nil {
- return nil, err
- }
- b.Write(by)
- _, err = b.Write([]byte("..."))
+ b, err := yaml.Marshal(in)
if err != nil {
- return nil, err
+ return err
}
- return b.Bytes(), nil
+
+ _, err = w.Write(b)
+ return err
+
case rune(TOMLLead[0]):
tree := toml.TreeFromMap(in.(map[string]interface{}))
- return []byte(tree.String()), nil
+ b := []byte(tree.String())
+
+ _, err := w.Write(b)
+ return err
+
case rune(JSONLead[0]):
- by, err := json.MarshalIndent(in, "", " ")
+ b, err := json.MarshalIndent(in, "", " ")
if err != nil {
- return nil, err
+ return err
}
- b.Write(by)
- _, err = b.Write([]byte("\n"))
+
+ _, err = w.Write(b)
if err != nil {
- return nil, err
+ return err
}
- return b.Bytes(), nil
+
+ _, err = w.Write([]byte{'\n'})
+ return err
+
default:
- return nil, errors.New("Unsupported Format provided")
+ return errors.New("Unsupported Format provided")
}
}
-func InterfaceToFrontMatter(in interface{}, mark rune) ([]byte, error) {
+// InterfaceToFrontMatter encodes a given input into a frontmatter
+// representation based upon the mark with the appropriate front matter delimiters
+// surrounding the output, which is written to w.
+func InterfaceToFrontMatter(in interface{}, mark rune, w io.Writer) error {
if in == nil {
- return []byte{}, errors.New("input was nil")
+ return errors.New("input was nil")
}
- b := new(bytes.Buffer)
-
switch mark {
case rune(YAMLLead[0]):
- _, err := b.Write([]byte(YAMLDelimUnix))
+ _, err := w.Write([]byte(YAMLDelimUnix))
if err != nil {
- return nil, err
+ return err
}
- by, err := yaml.Marshal(in)
- if err != nil {
- return nil, err
- }
- b.Write(by)
- _, err = b.Write([]byte(YAMLDelimUnix))
+
+ err = InterfaceToConfig(in, mark, w)
if err != nil {
- return nil, err
+ return err
}
- return b.Bytes(), nil
+
+ _, err = w.Write([]byte(YAMLDelimUnix))
+ return err
+
case rune(TOMLLead[0]):
- _, err := b.Write([]byte(TOMLDelimUnix))
+ _, err := w.Write([]byte(TOMLDelimUnix))
if err != nil {
- return nil, err
+ return err
}
- tree := toml.TreeFromMap(in.(map[string]interface{}))
- b.Write([]byte(tree.String()))
- _, err = b.Write([]byte("\n" + TOMLDelimUnix))
- if err != nil {
- return nil, err
- }
- return b.Bytes(), nil
- case rune(JSONLead[0]):
- by, err := json.MarshalIndent(in, "", " ")
- if err != nil {
- return nil, err
- }
- b.Write(by)
- _, err = b.Write([]byte("\n"))
+ err = InterfaceToConfig(in, mark, w)
if err != nil {
- return nil, err
+ return err
}
- return b.Bytes(), nil
+
+ _, err = w.Write([]byte("\n" + TOMLDelimUnix))
+ return err
+
default:
- return nil, errors.New("Unsupported Format provided")
+ return InterfaceToConfig(in, mark, w)
}
}
+// FormatToLeadRune takes a given format kind and return the leading front
+// matter delimiter.
func FormatToLeadRune(kind string) rune {
switch FormatSanitize(kind) {
case "yaml":
return rune([]byte(YAMLLead)[0])
case "json":
return rune([]byte(JSONLead)[0])
+ case "org":
+ return '#'
default:
return rune([]byte(TOMLLead)[0])
}
}
+// FormatSanitize returns the canonical format name for a given kind.
+//
// TODO(bep) move to helpers
func FormatSanitize(kind string) string {
switch strings.ToLower(kind) {
@@ -141,27 +145,31 @@ func FormatSanitize(kind string) string {
return "toml"
case "json", "js":
return "json"
+ case "org":
+ return kind
default:
return "toml"
}
}
// DetectFrontMatter detects the type of frontmatter analysing its first character.
-func DetectFrontMatter(mark rune) (f *frontmatterType) {
+func DetectFrontMatter(mark rune) (f *FrontmatterType) {
switch mark {
case '-':
- return &frontmatterType{[]byte(YAMLDelim), []byte(YAMLDelim), HandleYAMLMetaData, false}
+ return &FrontmatterType{HandleYAMLMetaData, []byte(YAMLDelim), []byte(YAMLDelim), false}
case '+':
- return &frontmatterType{[]byte(TOMLDelim), []byte(TOMLDelim), HandleTOMLMetaData, false}
+ return &FrontmatterType{HandleTOMLMetaData, []byte(TOMLDelim), []byte(TOMLDelim), false}
case '{':
- return &frontmatterType{[]byte{'{'}, []byte{'}'}, HandleJSONMetaData, true}
+ return &FrontmatterType{HandleJSONMetaData, []byte{'{'}, []byte{'}'}, true}
case '#':
- return &frontmatterType{[]byte("#+"), []byte("\n"), HandleOrgMetaData, false}
+ return &FrontmatterType{HandleOrgMetaData, []byte("#+"), []byte("\n"), false}
default:
return nil
}
}
+// HandleTOMLMetaData unmarshals TOML-encoded datum and returns a Go interface
+// representing the encoded data structure.
func HandleTOMLMetaData(datum []byte) (interface{}, error) {
m := map[string]interface{}{}
datum = removeTOMLIdentifier(datum)
@@ -177,22 +185,49 @@ func HandleTOMLMetaData(datum []byte) (interface{}, error) {
return m, nil
}
+// removeTOMLIdentifier removes, if necessary, beginning and ending TOML
+// frontmatter delimiters from a byte slice.
func removeTOMLIdentifier(datum []byte) []byte {
- return bytes.Replace(datum, []byte(TOMLDelim), []byte(""), -1)
+ ld := len(datum)
+ if ld < 8 {
+ return datum
+ }
+
+ b := bytes.TrimPrefix(datum, []byte(TOMLDelim))
+ if ld-len(b) != 3 {
+ // No TOML prefix trimmed, so bail out
+ return datum
+ }
+
+ b = bytes.Trim(b, "\r\n")
+ return bytes.TrimSuffix(b, []byte(TOMLDelim))
}
+// HandleYAMLMetaData unmarshals YAML-encoded datum and returns a Go interface
+// representing the encoded data structure.
func HandleYAMLMetaData(datum []byte) (interface{}, error) {
m := map[string]interface{}{}
err := yaml.Unmarshal(datum, &m)
return m, err
}
+// HandleJSONMetaData unmarshals JSON-encoded datum and returns a Go interface
+// representing the encoded data structure.
func HandleJSONMetaData(datum []byte) (interface{}, error) {
+ if datum == nil {
+ // Package json returns on error on nil input.
+ // Return an empty map to be consistent with our other supported
+ // formats.
+ return make(map[string]interface{}), nil
+ }
+
var f interface{}
err := json.Unmarshal(datum, &f)
return f, err
}
+// HandleOrgMetaData unmarshals org-mode encoded datum and returns a Go
+// interface representing the encoded data structure.
func HandleOrgMetaData(datum []byte) (interface{}, error) {
return goorgeous.OrgHeaders(datum)
}