moving front matter parsing (and creation) to parse package
authorspf13 <steve.francia@gmail.com>
Thu, 1 May 2014 17:19:51 +0000 (13:19 -0400)
committerspf13 <steve.francia@gmail.com>
Thu, 1 May 2014 17:20:09 +0000 (13:20 -0400)
hugolib/page.go
parser/frontmatter.go [new file with mode: 0644]
parser/page.go

index 679f13ebfa0184093de0265a45e1ad71f2f3c148..1581091cb64ef5774bc83cb321542836e43ce7f2 100644 (file)
@@ -24,35 +24,35 @@ import (
        "strings"
        "time"
 
-       "github.com/BurntSushi/toml"
        "github.com/spf13/cast"
        "github.com/spf13/hugo/helpers"
        "github.com/spf13/hugo/parser"
        jww "github.com/spf13/jwalterweatherman"
        "github.com/spf13/viper"
        "github.com/theplant/blackfriday"
-       "launchpad.net/goyaml"
-       json "launchpad.net/rjson"
 )
 
 type Page struct {
-       Status          string
-       Images          []string
-       rawContent      []byte
-       Content         template.HTML
-       Summary         template.HTML
-       TableOfContents template.HTML
-       Truncated       bool
-       plain           string // TODO should be []byte
-       Params          map[string]interface{}
-       contentType     string
-       Draft           bool
-       Aliases         []string
-       Tmpl            Template
-       Markup          string
-       renderable      bool
-       layout          string
-       linkTitle       string
+       Status            string
+       Images            []string
+       rawContent        []byte
+       Content           template.HTML
+       Summary           template.HTML
+       TableOfContents   template.HTML
+       Truncated         bool
+       plain             string // TODO should be []byte
+       Params            map[string]interface{}
+       contentType       string
+       Draft             bool
+       Aliases           []string
+       Tmpl              Template
+       Markup            string
+       renderable        bool
+       layout            string
+       linkTitle         string
+       frontmatter       []byte
+       sourceFrontmatter []byte
+       sourceContent     []byte
        PageMeta
        File
        Position
@@ -293,35 +293,6 @@ func (p *Page) RelPermalink() (string, error) {
        return link.String(), nil
 }
 
-func (page *Page) handleTomlMetaData(datum []byte) (interface{}, error) {
-       m := map[string]interface{}{}
-       datum = removeTomlIdentifier(datum)
-       if _, err := toml.Decode(string(datum), &m); err != nil {
-               return m, fmt.Errorf("Invalid TOML in %s \nError parsing page meta data: %s", page.FileName, err)
-       }
-       return m, nil
-}
-
-func removeTomlIdentifier(datum []byte) []byte {
-       return bytes.Replace(datum, []byte("+++"), []byte(""), -1)
-}
-
-func (page *Page) handleYamlMetaData(datum []byte) (interface{}, error) {
-       m := map[string]interface{}{}
-       if err := goyaml.Unmarshal(datum, &m); err != nil {
-               return m, fmt.Errorf("Invalid YAML in %s \nError parsing page meta data: %s", page.FileName, err)
-       }
-       return m, nil
-}
-
-func (page *Page) handleJsonMetaData(datum []byte) (interface{}, error) {
-       var f interface{}
-       if err := json.Unmarshal(datum, &f); err != nil {
-               return f, fmt.Errorf("Invalid JSON in %v \nError parsing page meta data: %s", page.FileName, err)
-       }
-       return f, nil
-}
-
 func (page *Page) update(f interface{}) error {
        m := f.(map[string]interface{})
 
@@ -499,28 +470,6 @@ func (page *Page) Menus() PageMenus {
        return nil
 }
 
-type frontmatterType struct {
-       markstart, markend []byte
-       parse              func([]byte) (interface{}, error)
-       includeMark        bool
-}
-
-const YAML_DELIM = "---"
-const TOML_DELIM = "+++"
-
-func (page *Page) detectFrontMatter(mark rune) (f *frontmatterType) {
-       switch mark {
-       case '-':
-               return &frontmatterType{[]byte(YAML_DELIM), []byte(YAML_DELIM), page.handleYamlMetaData, false}
-       case '+':
-               return &frontmatterType{[]byte(TOML_DELIM), []byte(TOML_DELIM), page.handleTomlMetaData, false}
-       case '{':
-               return &frontmatterType{[]byte{'{'}, []byte{'}'}, page.handleJsonMetaData, true}
-       default:
-               return nil
-       }
-}
-
 func (p *Page) Render(layout ...string) template.HTML {
        curLayout := ""
 
@@ -573,30 +522,45 @@ func guessType(in string) string {
        return "unknown"
 }
 
+func (page *Page) detectFrontMatter() (f *parser.FrontmatterType) {
+       return parser.DetectFrontMatter(rune(page.frontmatter[0]))
+}
+
 func (page *Page) parse(reader io.Reader) error {
-       p, err := parser.ReadFrom(reader)
+       psr, err := parser.ReadFrom(reader)
        if err != nil {
                return err
        }
 
-       page.renderable = p.IsRenderable()
+       page.renderable = psr.IsRenderable()
+       page.frontmatter = psr.FrontMatter()
+       meta, err := psr.Metadata()
+       if err != nil {
+               jww.ERROR.Printf("Error parsing page meta data for %s", page.FileName)
+               jww.ERROR.Println(err)
+               return err
+       }
+       if err = page.update(meta); err != nil {
+               return err
+       }
 
-       front := p.FrontMatter()
+       page.rawContent = psr.Content()
+       page.setSummary()
 
-       if len(front) != 0 {
-               fm := page.detectFrontMatter(rune(front[0]))
-               meta, err := fm.parse(front)
-               if err != nil {
-                       return err
-               }
+       return nil
+}
 
-               if err = page.update(meta); err != nil {
-                       return err
-               }
+func (page *Page) SetSourceContent(content []byte) {
+       page.sourceContent = content
+}
 
+func (page *Page) SetSourceMetaData(in interface{}, mark rune) (err error) {
+       by, err := parser.InterfaceToFrontMatter(in, mark)
+       if err != nil {
+               return err
        }
-       page.rawContent = p.Content()
-       page.setSummary()
+
+       page.sourceFrontmatter = by
 
        return nil
 }
diff --git a/parser/frontmatter.go b/parser/frontmatter.go
new file mode 100644 (file)
index 0000000..6ace003
--- /dev/null
@@ -0,0 +1,129 @@
+// Copyright © 2013 Steve Francia <spf@spf13.com>.
+//
+// Licensed under the Simple Public License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://opensource.org/licenses/Simple-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package parser
+
+import (
+       "bytes"
+       "encoding/json"
+       "fmt"
+
+       "github.com/BurntSushi/toml"
+       "launchpad.net/goyaml"
+)
+
+type FrontmatterType struct {
+       markstart, markend []byte
+       Parse              func([]byte) (interface{}, error)
+       includeMark        bool
+}
+
+func InterfaceToFrontMatter(in interface{}, mark rune) ([]byte, error) {
+       if in == nil {
+               return []byte{}, fmt.Errorf("input was nil")
+       }
+
+       b := new(bytes.Buffer)
+
+       switch mark {
+       case rune(YAML_LEAD[0]):
+               _, err := b.Write([]byte(YAML_DELIM_UNIX))
+               if err != nil {
+                       return nil, err
+               }
+               by, err := goyaml.Marshal(in)
+               if err != nil {
+                       return nil, err
+               }
+               b.Write(by)
+               _, err = b.Write([]byte(YAML_DELIM_UNIX))
+               if err != nil {
+                       return nil, err
+               }
+               return b.Bytes(), nil
+       case rune(TOML_LEAD[0]):
+               _, err := b.Write([]byte(TOML_DELIM_UNIX))
+               if err != nil {
+                       return nil, err
+               }
+
+               err = toml.NewEncoder(b).Encode(in)
+               if err != nil {
+                       fmt.Println("toml encoder failed", in)
+                       fmt.Println(err)
+                       return nil, err
+               }
+               _, err = b.Write([]byte("\n" + TOML_DELIM_UNIX))
+               if err != nil {
+                       return nil, err
+               }
+               return b.Bytes(), nil
+       case rune(JSON_LEAD[0]):
+               by, err := json.MarshalIndent(in, "", "   ")
+               if err != nil {
+                       fmt.Println("json encoder failed", in)
+                       fmt.Println(err)
+                       return nil, err
+               }
+               b.Write(by)
+               _, err = b.Write([]byte("\n"))
+               if err != nil {
+                       return nil, err
+               }
+               return b.Bytes(), nil
+       default:
+               return nil, fmt.Errorf("Unsupported Format provided")
+       }
+}
+
+func DetectFrontMatter(mark rune) (f *FrontmatterType) {
+       switch mark {
+       case '-':
+               return &FrontmatterType{[]byte(YAML_DELIM), []byte(YAML_DELIM), HandleYamlMetaData, false}
+       case '+':
+               return &FrontmatterType{[]byte(TOML_DELIM), []byte(TOML_DELIM), HandleTomlMetaData, false}
+       case '{':
+               return &FrontmatterType{[]byte{'{'}, []byte{'}'}, HandleJsonMetaData, true}
+       default:
+               return nil
+       }
+}
+
+func HandleTomlMetaData(datum []byte) (interface{}, error) {
+       m := map[string]interface{}{}
+       datum = removeTomlIdentifier(datum)
+       if _, err := toml.Decode(string(datum), &m); err != nil {
+               return m, err
+       }
+       return m, nil
+}
+
+func removeTomlIdentifier(datum []byte) []byte {
+       return bytes.Replace(datum, []byte(TOML_DELIM), []byte(""), -1)
+}
+
+func HandleYamlMetaData(datum []byte) (interface{}, error) {
+       m := map[string]interface{}{}
+       if err := goyaml.Unmarshal(datum, &m); err != nil {
+               return m, err
+       }
+       return m, nil
+}
+
+func HandleJsonMetaData(datum []byte) (interface{}, error) {
+       var f interface{}
+       if err := json.Unmarshal(datum, &f); err != nil {
+               return f, err
+       }
+       return f, nil
+}
index c0e1eebc448efc0cfdae40dfa5c2b82434a4548e..a3c0ec7d7de80c9474f44978ef79b05c8e627216 100644 (file)
@@ -13,9 +13,11 @@ const (
        YAML_LEAD       = "-"
        YAML_DELIM_UNIX = "---\n"
        YAML_DELIM_DOS  = "---\r\n"
+       YAML_DELIM      = "---"
        TOML_LEAD       = "+"
        TOML_DELIM_UNIX = "+++\n"
        TOML_DELIM_DOS  = "+++\r\n"
+       TOML_DELIM      = "+++"
        JSON_LEAD       = "{"
 )
 
@@ -39,6 +41,7 @@ type Page interface {
        FrontMatter() FrontMatter
        Content() Content
        IsRenderable() bool
+       Metadata() (interface{}, error)
 }
 
 type page struct {
@@ -59,6 +62,19 @@ func (p *page) IsRenderable() bool {
        return p.render
 }
 
+func (p *page) Metadata() (meta interface{}, err error) {
+       frontmatter := p.FrontMatter()
+
+       if len(frontmatter) != 0 {
+               fm := DetectFrontMatter(rune(frontmatter[0]))
+               meta, err = fm.Parse(frontmatter)
+               if err != nil {
+                       return
+               }
+       }
+       return
+}
+
 // ReadFrom reads the content from an io.Reader and constructs a page.
 func ReadFrom(r io.Reader) (p Page, err error) {
        reader := bufio.NewReader(r)