| 
									
										
										
										
											2016-03-23 14:51:16 +01:00
										 |  |  | // Copyright 2016n The Hugo Authors. All rights reserved.
 | 
					
						
							| 
									
										
										
										
											2015-12-10 15:19:38 -07:00
										 |  |  | //
 | 
					
						
							|  |  |  | // Licensed under the Apache 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://www.apache.org/licenses/LICENSE-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.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | package parser
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import (
 | 
					
						
							|  |  |  | 	"bufio"
 | 
					
						
							|  |  |  | 	"bytes"
 | 
					
						
							| 
									
										
										
										
											2013-09-18 09:15:46 -07:00
										 |  |  | 	"fmt"
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | 	"io"
 | 
					
						
							| 
									
										
										
										
											2015-08-01 22:24:22 -07:00
										 |  |  | 	"regexp"
 | 
					
						
							| 
									
										
										
										
											2015-09-23 14:43:17 +10:00
										 |  |  | 	"strings"
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | 	"unicode"
 | 
					
						
							|  |  |  | )
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const (
 | 
					
						
							| 
									
										
										
										
											2016-03-23 14:51:16 +01:00
										 |  |  | 	// TODO(bep) Do we really have to export these?
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// HTMLLead identifies the start of HTML documents.
 | 
					
						
							|  |  |  | 	HTMLLead = "<"
 | 
					
						
							|  |  |  | 	// YAMLLead identifies the start of YAML frontmatter.
 | 
					
						
							|  |  |  | 	YAMLLead = "-"
 | 
					
						
							|  |  |  | 	// YAMLDelimUnix identifies the end of YAML front matter on Unix.
 | 
					
						
							|  |  |  | 	YAMLDelimUnix = "---\n"
 | 
					
						
							|  |  |  | 	// YAMLDelimDOS identifies the end of YAML front matter on Windows.
 | 
					
						
							|  |  |  | 	YAMLDelimDOS = "---\r\n"
 | 
					
						
							|  |  |  | 	// YAMLDelim identifies the YAML front matter delimiter.
 | 
					
						
							|  |  |  | 	YAMLDelim = "---"
 | 
					
						
							|  |  |  | 	// TOMLLead identifies the start of TOML front matter.
 | 
					
						
							|  |  |  | 	TOMLLead = "+"
 | 
					
						
							|  |  |  | 	// TOMLDelimUnix identifies the end of TOML front matter on Unix.
 | 
					
						
							|  |  |  | 	TOMLDelimUnix = "+++\n"
 | 
					
						
							|  |  |  | 	// TOMLDelimDOS identifies the end of TOML front matter on Windows.
 | 
					
						
							|  |  |  | 	TOMLDelimDOS = "+++\r\n"
 | 
					
						
							|  |  |  | 	// TOMLDelim identifies the TOML front matter delimiter.
 | 
					
						
							|  |  |  | 	TOMLDelim = "+++"
 | 
					
						
							|  |  |  | 	// JSONLead identifies the start of JSON frontmatter.
 | 
					
						
							|  |  |  | 	JSONLead = "{"
 | 
					
						
							|  |  |  | 	// HTMLCommentStart identifies the start of HTML comment.
 | 
					
						
							|  |  |  | 	HTMLCommentStart = "<!--"
 | 
					
						
							|  |  |  | 	// HTMLCommentEnd identifies the end of HTML comment.
 | 
					
						
							|  |  |  | 	HTMLCommentEnd = "-->"
 | 
					
						
							| 
									
										
										
										
											2016-04-13 00:14:00 +02:00
										 |  |  | 	// BOM Unicode byte order marker
 | 
					
						
							|  |  |  | 	BOM = '\ufeff'
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | )
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var (
 | 
					
						
							| 
									
										
										
										
											2015-08-01 22:24:22 -07:00
										 |  |  | 	delims = regexp.MustCompile(
 | 
					
						
							| 
									
										
										
										
											2016-03-23 14:51:16 +01:00
										 |  |  | 		"^(" + regexp.QuoteMeta(YAMLDelim) + `\s*\n|` + regexp.QuoteMeta(TOMLDelim) + `\s*\n|` + regexp.QuoteMeta(JSONLead) + ")",
 | 
					
						
							| 
									
										
										
										
											2015-08-01 22:24:22 -07:00
										 |  |  | 	)
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | )
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-23 14:51:16 +01:00
										 |  |  | // Page represents a parsed content page.
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | type Page interface {
 | 
					
						
							| 
									
										
										
										
											2016-03-23 14:51:16 +01:00
										 |  |  | 	FrontMatter() []byte
 | 
					
						
							|  |  |  | 	Content() []byte
 | 
					
						
							| 
									
										
										
										
											2013-09-18 10:17:43 -07:00
										 |  |  | 	IsRenderable() bool
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | 	Metadata() (interface{}, error)
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type page struct {
 | 
					
						
							|  |  |  | 	render      bool
 | 
					
						
							| 
									
										
										
										
											2016-03-23 14:51:16 +01:00
										 |  |  | 	frontmatter []byte
 | 
					
						
							|  |  |  | 	content     []byte
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-23 14:51:16 +01:00
										 |  |  | func (p *page) Content() []byte {
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | 	return p.content
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-23 14:51:16 +01:00
										 |  |  | func (p *page) FrontMatter() []byte {
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | 	return p.frontmatter
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-18 10:17:43 -07:00
										 |  |  | func (p *page) IsRenderable() bool {
 | 
					
						
							|  |  |  | 	return p.render
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | func (p *page) Metadata() (meta interface{}, err error) {
 | 
					
						
							|  |  |  | 	frontmatter := p.FrontMatter()
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if len(frontmatter) != 0 {
 | 
					
						
							| 
									
										
										
										
											2016-04-26 21:21:15 +01:00
										 |  |  | 		fm := DetectFrontMatter(rune(frontmatter[0]))
 | 
					
						
							| 
									
										
										
										
											2014-05-01 13:19:51 -04:00
										 |  |  | 		meta, err = fm.Parse(frontmatter)
 | 
					
						
							|  |  |  | 		if err != nil {
 | 
					
						
							|  |  |  | 			return
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	return
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | // 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)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-13 00:14:00 +02:00
										 |  |  | 	// chomp BOM and assume UTF-8
 | 
					
						
							|  |  |  | 	if err = chompBOM(reader); err != nil && err != io.EOF {
 | 
					
						
							|  |  |  | 		return
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2013-12-16 10:34:26 +02:00
										 |  |  | 	if err = chompWhitespace(reader); err != nil && err != io.EOF {
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | 		return
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2015-09-23 14:43:17 +10:00
										 |  |  | 	if err = chompFrontmatterStartComment(reader); err != nil && err != io.EOF {
 | 
					
						
							|  |  |  | 		return
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	firstLine, err := peekLine(reader)
 | 
					
						
							| 
									
										
										
										
											2013-12-16 10:34:26 +02:00
										 |  |  | 	if err != nil && err != io.EOF {
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | 		return
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	newp := new(page)
 | 
					
						
							|  |  |  | 	newp.render = shouldRender(firstLine)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if newp.render && isFrontMatterDelim(firstLine) {
 | 
					
						
							|  |  |  | 		left, right := determineDelims(firstLine)
 | 
					
						
							|  |  |  | 		fm, err := extractFrontMatterDelims(reader, left, right)
 | 
					
						
							|  |  |  | 		if err != nil {
 | 
					
						
							|  |  |  | 			return nil, err
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		newp.frontmatter = fm
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	content, err := extractContent(reader)
 | 
					
						
							|  |  |  | 	if err != nil {
 | 
					
						
							|  |  |  | 		return nil, err
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	newp.content = content
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return newp, nil
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-13 00:14:00 +02:00
										 |  |  | func chompBOM(r io.RuneScanner) (err error) {
 | 
					
						
							|  |  |  | 	for {
 | 
					
						
							|  |  |  | 		c, _, err := r.ReadRune()
 | 
					
						
							|  |  |  | 		if err != nil {
 | 
					
						
							|  |  |  | 			return err
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		if c != BOM {
 | 
					
						
							|  |  |  | 			r.UnreadRune()
 | 
					
						
							|  |  |  | 			return nil
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | func chompWhitespace(r io.RuneScanner) (err error) {
 | 
					
						
							|  |  |  | 	for {
 | 
					
						
							|  |  |  | 		c, _, err := r.ReadRune()
 | 
					
						
							|  |  |  | 		if err != nil {
 | 
					
						
							|  |  |  | 			return err
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		if !unicode.IsSpace(c) {
 | 
					
						
							|  |  |  | 			r.UnreadRune()
 | 
					
						
							|  |  |  | 			return nil
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-23 14:43:17 +10:00
										 |  |  | func chompFrontmatterStartComment(r *bufio.Reader) (err error) {
 | 
					
						
							|  |  |  | 	candidate, err := r.Peek(32)
 | 
					
						
							|  |  |  | 	if err != nil {
 | 
					
						
							|  |  |  | 		return err
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	str := string(candidate)
 | 
					
						
							| 
									
										
										
										
											2016-03-23 14:51:16 +01:00
										 |  |  | 	if strings.HasPrefix(str, HTMLCommentStart) {
 | 
					
						
							| 
									
										
										
										
											2015-09-23 14:43:17 +10:00
										 |  |  | 		lineEnd := strings.IndexAny(str, "\n")
 | 
					
						
							|  |  |  | 		if lineEnd == -1 {
 | 
					
						
							|  |  |  | 			//TODO: if we can't find it, Peek more?
 | 
					
						
							|  |  |  | 			return nil
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		testStr := strings.TrimSuffix(str[0:lineEnd], "\r")
 | 
					
						
							| 
									
										
										
										
											2016-03-23 14:51:16 +01:00
										 |  |  | 		if strings.Index(testStr, HTMLCommentEnd) != -1 {
 | 
					
						
							| 
									
										
										
										
											2015-09-23 14:43:17 +10:00
										 |  |  | 			return nil
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		buf := make([]byte, lineEnd)
 | 
					
						
							|  |  |  | 		if _, err = r.Read(buf); err != nil {
 | 
					
						
							|  |  |  | 			return
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		if err = chompWhitespace(r); err != nil {
 | 
					
						
							|  |  |  | 			return err
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func chompFrontmatterEndComment(r *bufio.Reader) (err error) {
 | 
					
						
							|  |  |  | 	candidate, err := r.Peek(32)
 | 
					
						
							|  |  |  | 	if err != nil {
 | 
					
						
							|  |  |  | 		return err
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	str := string(candidate)
 | 
					
						
							|  |  |  | 	lineEnd := strings.IndexAny(str, "\n")
 | 
					
						
							|  |  |  | 	if lineEnd == -1 {
 | 
					
						
							|  |  |  | 		return nil
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	testStr := strings.TrimSuffix(str[0:lineEnd], "\r")
 | 
					
						
							| 
									
										
										
										
											2016-03-23 14:51:16 +01:00
										 |  |  | 	if strings.Index(testStr, HTMLCommentStart) != -1 {
 | 
					
						
							| 
									
										
										
										
											2015-09-23 14:43:17 +10:00
										 |  |  | 		return nil
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	//TODO: if we can't find it, Peek more?
 | 
					
						
							| 
									
										
										
										
											2016-03-23 14:51:16 +01:00
										 |  |  | 	if strings.HasSuffix(testStr, HTMLCommentEnd) {
 | 
					
						
							| 
									
										
										
										
											2015-09-23 14:43:17 +10:00
										 |  |  | 		buf := make([]byte, lineEnd)
 | 
					
						
							|  |  |  | 		if _, err = r.Read(buf); err != nil {
 | 
					
						
							|  |  |  | 			return
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		if err = chompWhitespace(r); err != nil {
 | 
					
						
							|  |  |  | 			return err
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | func peekLine(r *bufio.Reader) (line []byte, err error) {
 | 
					
						
							|  |  |  | 	firstFive, err := r.Peek(5)
 | 
					
						
							|  |  |  | 	if err != nil {
 | 
					
						
							|  |  |  | 		return
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	idx := bytes.IndexByte(firstFive, '\n')
 | 
					
						
							|  |  |  | 	if idx == -1 {
 | 
					
						
							|  |  |  | 		return firstFive, nil
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2015-03-07 12:59:04 +01:00
										 |  |  | 	idx++ // include newline.
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | 	return firstFive[:idx], nil
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func shouldRender(lead []byte) (frontmatter bool) {
 | 
					
						
							|  |  |  | 	if len(lead) <= 0 {
 | 
					
						
							|  |  |  | 		return
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-23 14:51:16 +01:00
										 |  |  | 	if bytes.Equal(lead[:1], []byte(HTMLLead)) {
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | 		return
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	return true
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func isFrontMatterDelim(data []byte) bool {
 | 
					
						
							| 
									
										
										
										
											2015-08-01 22:24:22 -07:00
										 |  |  | 	return delims.Match(data)
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func determineDelims(firstLine []byte) (left, right []byte) {
 | 
					
						
							|  |  |  | 	switch len(firstLine) {
 | 
					
						
							|  |  |  | 	case 5:
 | 
					
						
							| 
									
										
										
										
											2015-01-10 16:15:51 +09:00
										 |  |  | 		fallthrough
 | 
					
						
							|  |  |  | 	case 4:
 | 
					
						
							| 
									
										
										
										
											2016-03-23 14:51:16 +01:00
										 |  |  | 		if firstLine[0] == YAMLLead[0] {
 | 
					
						
							|  |  |  | 			return []byte(YAMLDelim), []byte(YAMLDelim)
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2016-03-23 14:51:16 +01:00
										 |  |  | 		return []byte(TOMLDelim), []byte(TOMLDelim)
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | 	case 3:
 | 
					
						
							|  |  |  | 		fallthrough
 | 
					
						
							|  |  |  | 	case 2:
 | 
					
						
							|  |  |  | 		fallthrough
 | 
					
						
							|  |  |  | 	case 1:
 | 
					
						
							| 
									
										
										
										
											2016-03-23 14:51:16 +01:00
										 |  |  | 		return []byte(JSONLead), []byte("}")
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | 	default:
 | 
					
						
							|  |  |  | 		panic(fmt.Sprintf("Unable to determine delims from %q", firstLine))
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-10 16:15:51 +09:00
										 |  |  | // extractFrontMatterDelims takes a frontmatter from the content bufio.Reader.
 | 
					
						
							| 
									
										
										
										
											2016-02-13 02:24:25 -07:00
										 |  |  | // Beginning white spaces of the bufio.Reader must be trimmed before call this
 | 
					
						
							| 
									
										
										
										
											2015-01-10 16:15:51 +09:00
										 |  |  | // function.
 | 
					
						
							| 
									
										
										
										
											2016-03-23 14:51:16 +01:00
										 |  |  | func extractFrontMatterDelims(r *bufio.Reader, left, right []byte) (fm []byte, err error) {
 | 
					
						
							| 
									
										
										
										
											2013-09-18 09:15:46 -07:00
										 |  |  | 	var (
 | 
					
						
							|  |  |  | 		c         byte
 | 
					
						
							| 
									
										
										
										
											2015-01-10 16:15:51 +09:00
										 |  |  | 		buf       bytes.Buffer
 | 
					
						
							| 
									
										
										
										
											2015-03-07 12:59:04 +01:00
										 |  |  | 		level     int
 | 
					
						
							|  |  |  | 		sameDelim = bytes.Equal(left, right)
 | 
					
						
							| 
									
										
										
										
											2013-09-18 09:15:46 -07:00
										 |  |  | 	)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-10 16:15:51 +09:00
										 |  |  | 	// Frontmatter must start with a delimiter. To check it first,
 | 
					
						
							|  |  |  | 	// pre-reads beginning delimiter length - 1 bytes from Reader
 | 
					
						
							|  |  |  | 	for i := 0; i < len(left)-1; i++ {
 | 
					
						
							|  |  |  | 		if c, err = r.ReadByte(); err != nil {
 | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("unable to read frontmatter at filepos %d: %s", buf.Len(), err)
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		if err = buf.WriteByte(c); err != nil {
 | 
					
						
							|  |  |  | 			return nil, err
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Reads a character from Reader one by one and checks it matches the
 | 
					
						
							| 
									
										
										
										
											2015-08-18 22:59:34 -03:00
										 |  |  | 	// last character of one of delimiters to find the last character of
 | 
					
						
							| 
									
										
										
										
											2015-01-10 16:15:51 +09:00
										 |  |  | 	// frontmatter. If it matches, makes sure it contains the delimiter
 | 
					
						
							|  |  |  | 	// and if so, also checks it is followed by CR+LF or LF when YAML,
 | 
					
						
							|  |  |  | 	// TOML case. In JSON case, nested delimiters must be parsed and it
 | 
					
						
							|  |  |  | 	// is expected that the delimiter only contains one character.
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | 	for {
 | 
					
						
							| 
									
										
										
										
											2013-09-18 09:15:46 -07:00
										 |  |  | 		if c, err = r.ReadByte(); err != nil {
 | 
					
						
							| 
									
										
										
										
											2015-01-10 16:15:51 +09:00
										 |  |  | 			return nil, fmt.Errorf("unable to read frontmatter at filepos %d: %s", buf.Len(), err)
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		if err = buf.WriteByte(c); err != nil {
 | 
					
						
							|  |  |  | 			return nil, err
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | 		}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		switch c {
 | 
					
						
							| 
									
										
										
										
											2015-01-10 16:15:51 +09:00
										 |  |  | 		case left[len(left)-1]:
 | 
					
						
							|  |  |  | 			if sameDelim { // YAML, TOML case
 | 
					
						
							| 
									
										
										
										
											2015-08-03 23:32:51 +09:00
										 |  |  | 				if bytes.HasSuffix(buf.Bytes(), left) && (buf.Len() == len(left) || buf.Bytes()[buf.Len()-len(left)-1] == '\n') {
 | 
					
						
							| 
									
										
										
										
											2015-08-01 22:24:22 -07:00
										 |  |  | 				nextByte:
 | 
					
						
							| 
									
										
										
										
											2015-01-10 16:15:51 +09:00
										 |  |  | 					c, err = r.ReadByte()
 | 
					
						
							|  |  |  | 					if err != nil {
 | 
					
						
							|  |  |  | 						// It is ok that the end delimiter ends with EOF
 | 
					
						
							|  |  |  | 						if err != io.EOF || level != 1 {
 | 
					
						
							|  |  |  | 							return nil, fmt.Errorf("unable to read frontmatter at filepos %d: %s", buf.Len(), err)
 | 
					
						
							|  |  |  | 						}
 | 
					
						
							|  |  |  | 					} else {
 | 
					
						
							|  |  |  | 						switch c {
 | 
					
						
							|  |  |  | 						case '\n':
 | 
					
						
							|  |  |  | 							// ok
 | 
					
						
							| 
									
										
										
										
											2015-08-01 22:24:22 -07:00
										 |  |  | 						case ' ':
 | 
					
						
							|  |  |  | 							// Consume this byte and try to match again
 | 
					
						
							|  |  |  | 							goto nextByte
 | 
					
						
							| 
									
										
										
										
											2015-01-10 16:15:51 +09:00
										 |  |  | 						case '\r':
 | 
					
						
							|  |  |  | 							if err = buf.WriteByte(c); err != nil {
 | 
					
						
							|  |  |  | 								return nil, err
 | 
					
						
							|  |  |  | 							}
 | 
					
						
							|  |  |  | 							if c, err = r.ReadByte(); err != nil {
 | 
					
						
							|  |  |  | 								return nil, fmt.Errorf("unable to read frontmatter at filepos %d: %s", buf.Len(), err)
 | 
					
						
							|  |  |  | 							}
 | 
					
						
							|  |  |  | 							if c != '\n' {
 | 
					
						
							|  |  |  | 								return nil, fmt.Errorf("frontmatter delimiter must be followed by CR+LF or LF but those can't be found at filepos %d", buf.Len())
 | 
					
						
							|  |  |  | 							}
 | 
					
						
							|  |  |  | 						default:
 | 
					
						
							|  |  |  | 							return nil, fmt.Errorf("frontmatter delimiter must be followed by CR+LF or LF but those can't be found at filepos %d", buf.Len())
 | 
					
						
							|  |  |  | 						}
 | 
					
						
							|  |  |  | 						if err = buf.WriteByte(c); err != nil {
 | 
					
						
							|  |  |  | 							return nil, err
 | 
					
						
							|  |  |  | 						}
 | 
					
						
							|  |  |  | 					}
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | 					if level == 0 {
 | 
					
						
							|  |  |  | 						level = 1
 | 
					
						
							|  |  |  | 					} else {
 | 
					
						
							|  |  |  | 						level = 0
 | 
					
						
							|  |  |  | 					}
 | 
					
						
							| 
									
										
										
										
											2013-09-18 09:15:46 -07:00
										 |  |  | 				}
 | 
					
						
							| 
									
										
										
										
											2015-01-10 16:15:51 +09:00
										 |  |  | 			} else { // JSON case
 | 
					
						
							|  |  |  | 				level++
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | 			}
 | 
					
						
							| 
									
										
										
										
											2015-01-10 16:15:51 +09:00
										 |  |  | 		case right[len(right)-1]: // JSON case only reaches here
 | 
					
						
							|  |  |  | 			level--
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | 		}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-10 16:15:51 +09:00
										 |  |  | 		if level == 0 {
 | 
					
						
							|  |  |  | 			// Consumes white spaces immediately behind frontmatter
 | 
					
						
							| 
									
										
										
										
											2015-09-23 14:43:17 +10:00
										 |  |  | 			if err = chompWhitespace(r); err != nil && err != io.EOF {
 | 
					
						
							|  |  |  | 				return nil, err
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | 			}
 | 
					
						
							| 
									
										
										
										
											2015-09-23 14:43:17 +10:00
										 |  |  | 			if err = chompFrontmatterEndComment(r); err != nil && err != io.EOF {
 | 
					
						
							|  |  |  | 				return nil, err
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-10 16:15:51 +09:00
										 |  |  | 			return buf.Bytes(), nil
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-23 14:51:16 +01:00
										 |  |  | func extractContent(r io.Reader) (content []byte, err error) {
 | 
					
						
							| 
									
										
										
										
											2013-09-17 15:52:40 -07:00
										 |  |  | 	wr := new(bytes.Buffer)
 | 
					
						
							|  |  |  | 	if _, err = wr.ReadFrom(r); err != nil {
 | 
					
						
							|  |  |  | 		return
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	return wr.Bytes(), nil
 | 
					
						
							|  |  |  | }
 |